注意:本章描述图像处理和分析的一些函数。大多数函数都是针对两维象素数组的,这里,我们称这些数组为“图像”,但是它们不一定非得是IplImage 结构,也可以是CvMat或者CvMatND结构。
目录[隐藏]
|
使用扩展 Sobel 算子计算一阶、二阶、三阶或混合图像差分
void cvSobel( const CvArr* src, CvArr* dst, int xorder, int yorder, int aperture_size=3 );
函数 cvSobel 通过对图像用相应的内核进行卷积操作来计算图像差分:
由于Sobel 算子结合了 Gaussian 平滑和微分,所以,其结果或多或少对噪声有一定的鲁棒性。通常情况,函数调用采用如下参数 (xorder=1, yorder=0, aperture_size=3) 或 (xorder=0, yorder=1, aperture_size=3) 来计算一阶 x- 或 y- 方向的图像差分。第一种情况对应:
核。
第二种对应:
或者
核的选则依赖于图像原点的定义 (origin 来自 IplImage 结构的定义)。由于该函数不进行图像尺度变换,所以和输入图像(数组)相比,输出图像(数组)的元素通常具有更大的绝对数值(译者注:即像素的位深)。为防止溢出,当输入图像是 8 位的,要求输出图像是 16 位的。当然可以用函数 cvConvertScale 或 cvConvertScaleAbs 把运算结果(dst)转换为 8 位的。除了8-位图像,函数也接受 32-位 浮点数图像。所有输入和输出图像都必须是单通道的,并且具有相同的图像尺寸或者ROI尺寸。
计算图像的 Laplacian 变换
void cvLaplace( const CvArr* src, CvArr* dst, int aperture_size=3 );
函数 cvLaplace 计算输入图像的 Laplacian变换,方法是先用 sobel 算子计算二阶 x- 和 y- 差分,再求和:
对 aperture_size=1 则给出最快计算结果,相当于对图像采用如下内核做卷积:
类似于 cvSobel 函数,该函数也不作图像的尺度变换,所支持的输入、输出图像类型的组合和cvSobel一致。
采用 Canny 算法做边缘检测
void cvCanny( const CvArr* image, CvArr* edges, double threshold1, double threshold2, int aperture_size=3 );
函数 cvCanny 采用 CANNY 算法发现输入图像的边缘而且在输出图像中标识这些边缘。threshold1和threshold2 当中的小阈值用来控制边缘连接,大的阈值用来控制强边缘的初始分割。
计算用于角点检测的特征图,
void cvPreCornerDetect( const CvArr* image, CvArr* corners, int aperture_size=3 );
函数 cvPreCornerDetect 计算函数 其中 表示一阶图像差分, 表示二阶图像差分。 角点被认为是函数的局部最大值:
// 假设图像格式为浮点数 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 ); cvSub( corners, dilated_corners, corners ); cvCmpS( corners, 0, corner_mask, CV_CMP_GE ); cvReleaseImage( &corners ); cvReleaseImage( &dilated_corners );
计算图像块的特征值和特征向量,用于角点检测
void cvCornerEigenValsAndVecs( const CvArr* image, CvArr* eigenvv, int block_size, int aperture_size=3 );
对每个象素,函数 cvCornerEigenValsAndVecs 考虑 block_size × block_size 大小的邻域 S(p),然后在邻域上计算图像差分的相关矩阵:
然后它计算矩阵的特征值和特征向量,并且按如下方式(λ1, λ2, x1, y1, x2, y2)存储这些值到输出图像中,其中
计算梯度矩阵的最小特征值,用于角点检测
void cvCornerMinEigenVal( const CvArr* image, CvArr* eigenval, int block_size, int aperture_size=3 );
函数 cvCornerMinEigenVal 与 cvCornerEigenValsAndVecs 类似,但是它仅仅计算和存储每个象素点差分相关矩阵的最小特征值,即前一个函数的 min(λ1, λ2)
哈里斯(Harris)角点检测
void cvCornerHarris( const CvArr* image, CvArr* harris_responce, int block_size, int aperture_size=3, double k=0.04 );
保存到输出图像中。输入图像中的角点在输出图像中由局部最大值表示。
精确角点位置
void cvFindCornerSubPix( const CvArr* image, CvPoint2D32f* corners, int count, CvSize win, CvSize zero_zone, CvTermCriteria criteria );
函数 cvFindCornerSubPix 通过迭代来发现具有子象素精度的角点位置,或如图所示的放射鞍点(radial saddle points)。
子象素级角点定位的实现是基于对向量正交性的观测而实现的,即从中央点q到其邻域点p 的向量和p点处的图像梯度正交(服从图像和测量噪声)。考虑以下的表达式:
εi=DIpiT•(q-pi)
其中,DIpi表示在q的一个邻域点pi处的图像梯度,q的值通过最小化εi得到。通过将εi设为0,可以建立系统方程如下:
sumi(DIpi•DIpiT)•q - sumi(DIpi•DIpiT•pi) = 0
其中q的邻域(搜索窗)中的梯度被累加。调用第一个梯度参数G和第二个梯度参数b,得到:
q=G-1•b
该算法将搜索窗的中心设为新的中心q,然后迭代,直到找到低于某个阈值点的中心位置。
确定图像的强角点
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 );
函数 cvGoodFeaturesToTrack 在图像中寻找具有大特征值的角点。该函数,首先用cvCornerMinEigenVal 计算输入图像的每一个象素点的最小特征值,并将结果存储到变量 eig_image 中。然后进行非最大值抑制(仅保留3x3邻域中的局部最大值)。下一步将最小特征值小于 quality_level•max(eig_image(x,y)) 排除掉。最后,函数确保所有发现的角点之间具有足够的距离,(最强的角点第一个保留,然后检查新的角点与已有角点之间的距离大于 min_distance )。
初始化线段迭代器
int cvInitLineIterator( const CvArr* image, CvPoint pt1, CvPoint pt2, CvLineIterator* line_iterator, int connectivity=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 ); }
将图像上某一光栅线上的像素数据读入缓冲区
int cvSampleLine( const CvArr* image, CvPoint pt1, CvPoint pt2, void* buffer, int connectivity=8 );
函数 cvSampleLine 实现了线段迭代器的一个特殊应用。它读取由 pt1 和 pt2 两点确定的线段上的所有图像点,包括终点,并存储到缓存中。
从图像中提取象素矩形,使用子象素精度
void cvGetRectSubPix( const CvArr* src, CvArr* dst, CvPoint2D32f center );
函数 cvGetRectSubPix 从图像 src 中提取矩形:
dst(x, y) = src(x + center.x - (width(dst)-1)*0.5, y + center.y - (height(dst)-1)*0.5)
其中非整数象素点坐标采用双线性插值提取。对多通道图像,每个通道独立单独完成提取。尽管函数要求矩形的中心一定要在输入图像之中,但是有可能出现矩形的一部分超出图像边界的情况,这时,该函数复制边界的模识(hunnish:即用于矩形相交的图像边界线段的象素来代替矩形超越部分的象素)。
提取象素四边形,使用子象素精度
void cvGetQuadrangleSubPix( const CvArr* src, CvArr* dst, const CvMat* map_matrix );
函数 cvGetQuadrangleSubPix 以子象素精度从图像 src 中提取四边形,使用子象素精度,并且将结果存储于 dst ,计算公式是:
dst(x + width(dst) / 2,y + height(dst) / 2) = src(A11x + A12y + b1,A21x + A22y + b2)
其中 A和 b 均来自映射矩阵(译者注:A, b为几何形变参数) ,映射矩阵为:
其中在非整数坐标 的象素点值通过双线性变换得到。当函数需要图像边界外的像素点时,使用重复边界模式(replication border mode)恢复出所需的值。多通道图像的每一个通道都单独计算。
例子:使用 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; }
图像大小变换
void cvResize( const CvArr* src, CvArr* dst, int interpolation=CV_INTER_LINEAR );
函数 cvResize 将图像 src 改变尺寸得到与 dst 同样大小。若设定 ROI,函数将按常规支持 ROI.
对图像做仿射变换
void cvWarpAffine( const CvArr* src, CvArr* dst, const CvMat* map_matrix, int flags=CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS, CvScalar fillval=cvScalarAll(0) );
函数 cvWarpAffine 利用下面指定的矩阵变换输入图像:
函数与 cvGetQuadrangleSubPix 类似,但是不完全相同。 cvWarpAffine 要求输入和输出图像具有同样的数据类型,有更大的资源开销(因此对小图像不太合适)而且输出图像的部分可以保留不变。而 cvGetQuadrangleSubPix 可以精确地从8位图像中提取四边形到浮点数缓存区中,具有比较小的系统开销,而且总是全部改变输出图像的内容。
要变换稀疏矩阵,使用 cxcore 中的函数 cvTransform 。
由三个不共线点计算仿射变换
CvMat* cvGetAffineTransform( const CvPoint2D32f* src, const CvPoint2D32f* dst, CvMat* map_matrix );
函数cvGetAffineTransform计算满足以下关系的仿射变换矩阵:
计算二维旋转的仿射变换矩阵
CvMat* cv2DRotationMatrix( CvPoint2D32f center, double angle, double scale, CvMat* map_matrix );
函数 cv2DRotationMatrix 计算矩阵:
[ α β | (1-α)*center.x - β*center.y ] [ -β α | β*center.x + (1-α)*center.y ] where α=scale*cos(angle), β=scale*sin(angle)
该变换并不改变原始旋转中心点的坐标,如果这不是操作目的,则可以通过调整平移量改变其坐标(译者注:通过简单的推导可知,仿射变换的实现是首先将旋转中心置为坐标原点,再进行旋转和尺度变换,最后重新将坐标原点设定为输入图像的左上角,这里的平移量是center.x, center.y).
对图像进行透视变换
void cvWarpPerspective( const CvArr* src, CvArr* dst, const CvMat* map_matrix, int flags=CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS, CvScalar fillval=cvScalarAll(0) );
函数 cvWarpPerspective 利用下面指定矩阵变换输入图像:
要变换稀疏矩阵,使用 cxcore 中的函数 cvTransform 。
用4个对应点计算透视变换矩阵
CvMat* cvWarpPerspectiveQMatrix( const CvPoint2D32f* src, const CvPoint2D32f* dst, CvMat* map_matrix );
函数 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.
由四边形的4个点计算透射变换
CvMat* cvGetPerspectiveTransform( const CvPoint2D32f* src, const CvPoint2D32f* dst, CvMat* map_matrix ); #define cvWarpPerspectiveQMatrix cvGetPerspectiveTransform
函数cvGetPerspectiveTransform计算满足以下关系的透射变换矩阵:
对图像进行普通几何变换
void cvRemap( const CvArr* src, CvArr* dst, const CvArr* mapx, const CvArr* mapy, int flags=CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS, CvScalar fillval=cvScalarAll(0) );
函数 cvRemap 利用下面指定的矩阵变换输入图像:
dst(x,y)<-src(mapx(x,y),mapy(x,y))
与其它几何变换类似,可以使用一些插值方法(由用户指定,译者注:同cvResize)来计算非整数坐标的像素值。
把图像映射到极指数空间
void cvLogPolar( const CvArr* src, CvArr* dst, CvPoint2D32f center, double M, int flags=CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS );
函数cvLogPolar用以下变换变换输入图像:
正变换 (CV_WARP_INVERSE_MAP 未置位):
dst(phi,rho)<-src(x,y)
逆变换 (CV_WARP_INVERSE_MAP 置位):
dst(x,y)<-src(phi,rho),
这里,
rho=M+log(sqrt(x2+y2)) phi=atan(y/x)
此函数模仿人类视网膜中央凹视力,并且对于目标跟踪等可用于快速尺度和旋转变换不变模板匹配。
Example. Log-polar transformation.
#include#include int main(int argc, char** argv) { IplImage* src; if(argc == 2 && ((src=cvLoadImage(argv[1],1)) != 0 )) { IplImage* dst = cvCreateImage( cvSize(256,256), 8, 3 ); IplImage* src2 = cvCreateImage( cvGetSize(src), 8, 3 ); cvLogPolar( src, dst, cvPoint2D32f(src->width/2,src->height/2), 40, CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS ); cvLogPolar( dst, src2, cvPoint2D32f(src->width/2,src->height/2), 40, CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS+CV_WARP_INVERSE_MAP ); cvNamedWindow( "log-polar", 1 ); cvShowImage( "log-polar", dst ); cvNamedWindow( "inverse log-polar", 1 ); cvShowImage( "inverse log-polar", src2 ); cvWaitKey(); } return 0; }
And this is what the program displays when opencv/samples/c/fruits.jpg is passed to it
创建结构元素
IplConvKernel* cvCreateStructuringElementEx( int cols, int rows, int anchor_x, int anchor_y, int shape, int* values=NULL );
函数 cv CreateStructuringElementEx 分配和填充结构 IplConvKernel, 它可作为形态操作中的结构元素。
删除结构元素
void cvReleaseStructuringElement( IplConvKernel** element );
函数 cvReleaseStructuringElement 释放结构 IplConvKernel 。如果 *element 为 NULL, 则函数不作用。
使用任意结构元素腐蚀图像
void cvErode( const CvArr* src, CvArr* dst, IplConvKernel* element=NULL, int iterations=1 );
函数 cvErode 对输入图像使用指定的结构元素进行腐蚀,该结构元素决定每个具有最小值象素点的邻域形状:
dst=erode(src,element): dst(x,y)=min((x',y') in element))src(x+x',y+y')
函数可能是本地操作,不需另外开辟存储空间的意思。腐蚀可以重复进行 (iterations) 次. 对彩色图像,每个彩色通道单独处理。
使用任意结构元素膨胀图像
void cvDilate( const CvArr* src, CvArr* dst, IplConvKernel* element=NULL, int iterations=1 );
函数 cvDilate 对输入图像使用指定的结构元进行膨胀,该结构决定每个具有最小值象素点的邻域形状:
dst=dilate(src,element): dst(x,y)=max((x',y') in element))src(x+x',y+y')
函数支持(in-place)模式。膨胀可以重复进行 (iterations) 次. 对彩色图像,每个彩色通道单独处理。
高级形态学变换
void cvMorphologyEx( const CvArr* src, CvArr* dst, CvArr* temp, IplConvKernel* element, int operation, int iterations=1 );
函数 cvMorphologyEx 在膨胀和腐蚀基本操作的基础上,完成一些高级的形态变换:
临时图像 temp 在形态梯度以及对“顶帽”和“黑帽”操作时的 in-place 模式下需要。
各种方法的图像平滑
void cvSmooth( const CvArr* src, CvArr* dst, int smoothtype=CV_GAUSSIAN, int param1=3, int param2=0, double param3=0, double param4=0 );
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 方式处理图像.
中值滤波法对消除椒盐噪音非常有效,在光学测量条纹图象的相位分析处理方法中有特殊作用,但在条纹中心分析方法中作用不大。中值滤波在图像处理中,常用于用来保护边缘信息,是经典的平滑噪声的方法
中值滤波是基于排序统计理论的一种能有效抑制噪声的非线性信号处理技术,中值滤波的基本原理是把数字图像或数字序列中一点的值用该点的一个拎域中各点值的中值代替,让周围的像素值接近的值,从而消除孤立的噪声点。方法是去某种结构的二维滑动模板,将板内像素按照像素值的大小进行排序,生成单调上升(或下降)的为二维数据序列。二维中值滤波输出为g(x,y)=med{f(x-k,y-l),(k,l∈W)} ,其中,f(x,y),g(x,y)分别为原始图像和处理后图像。W为二维模板,通常为2*2,3*3区域,也可以是不同的的形状,如线状,圆形,十字形,圆环形等。
高斯滤波实质上是一种信号的滤波器,其用途是信号的平滑处理,我们知道数字图像用于后期应用,其噪声是最大的问题,由于误差会累计传递等原因,很多图像处理教材会在很早的时候介绍Gauss滤波器,用于得到信噪比SNR较高的图像(反应真实信号)。于此相关的有Gauss-Lapplace变换,其实就是为了得到较好的图像边缘,先对图像做Gauss平滑滤波,剔除噪声,然后求二阶导矢,用二阶导的过零点确定边缘,在计算时也是频域乘积=>空域卷积。
滤波器就是建立的一个数学模型,通过这个模型来将图像数据进行能量转化,能量低的就排除掉,噪声就是属于低能量部分
其实编程运算的话就是一个模板运算,拿图像的八连通区域来说,中间点的像素值就等于八连通区的像素值的均值,这样达到平滑的效果
若使用理想滤波器,会在图像中产生振铃现象。采用高斯滤波器的话,系统函数是平滑的,避免了振铃现象。
对图像做卷积
void cvFilter2D( const CvArr* src, CvArr* dst, const CvMat* kernel, CvPoint anchor=cvPoint(-1,-1));
函数 cvFilter2D 对图像进行线性滤波,支持 In-place 操作。当核运算部分超出输入图像时,函数从最近邻的图像内部象素插值得到边界外面的象素值。
复制图像并且制作边界。
void cvCopyMakeBorder( const CvArr* src, CvArr* dst, CvPoint offset, int bordertype, CvScalar value=cvScalarAll(0) );
函数cvCopyMakeBorder拷贝输入2维阵列到输出阵列的内部并且在拷贝区域的周围制作一个指定类型的边界。函数可以用来模拟和嵌入在指定算法实现中的边界不同的类型。例如:和opencv中大多数其他滤波函数一样,一些形态学函数内部使用复制边界类型,但是用户可能需要零边界或者填充为1或255的边界。
计算积分图像
void cvIntegral( const CvArr* image, CvArr* sum, CvArr* sqsum=NULL, CvArr* tilted_sum=NULL );
函数 cvIntegral 计算一次或高次积分图像:
sum(X,Y) = | ∑ | image(x,y) |
x < X,y < Y |
sqsum(X,Y) = | ∑ | image(x,y)2 |
x < X,y < Y |
tilted_sum(X,Y) = | ∑ | image(x,y) |
y < Y, | x − X | < y |
利用积分图像,可以计算在某象素的上-右方的或者旋转的矩形区域中进行求和、求均值以及标准方差的计算,并且保证运算的复杂度为O(1)。例如:
因此可以在变化的窗口内做快速平滑或窗口相关等操作。
色彩空间转换
void cvCvtColor( const CvArr* src, CvArr* dst, int code );
函数 cvCvtColor 将输入图像从一个色彩空间转换为另外一个色彩空间。函数忽略 IplImage 头中定义的 colorModel 和 channelSeq 域,所以输入图像的色彩空间应该正确指定 (包括通道的顺序,对RGB空间而言,BGR 意味着布局为 B0 G0 R0 B1 G1 R1 ... 层叠的 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
RGB=>HLS (CV_BGR2HLS, CV_RGB2HLS)
HSL 表示 hue(色相)、saturation(饱和度)、lightness(亮度)。有的地方也称为HSI,其中I表示intensity(强度)
转换公式见http://zh.wikipedia.org/wiki/HSL_%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4
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"类型。
对数组元素进行固定阈值操作
void cvThreshold( const CvArr* src, CvArr* dst, double threshold, double max_value, int 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
下面是图形化的阈值描述:
自适应阈值方法
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 );
函数 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。
图像的下采样
void cvPyrDown( const CvArr* src, CvArr* dst, int filter=CV_GAUSSIAN_5x5 );
函数 cvPyrDown 使用 Gaussian 金字塔分解对输入图像向下采样。首先它对输入图像用指定滤波器进行卷积,然后通过拒绝偶数的行与列来下采样图像。
图像的上采样
void cvPyrUp( const CvArr* src, CvArr* dst, int filter=CV_GAUSSIAN_5x5 );
函数 cvPyrUp 使用Gaussian 金字塔分解对输入图像向上采样。首先通过在图像中插入 0 偶数行和偶数列,然后对得到的图像用指定的滤波器进行高斯卷积,其中滤波器乘以4做插值。所以输出图像是输入图像的 4 倍大小。(hunnish: 原理不清楚,尚待探讨)
连接部件
typedef struct CvConnectedComp { double area; /* 连通域的面积 */ float value; /* 分割域的灰度缩放值 */ CvRect rect; /* 分割域的 ROI */ } CvConnectedComp;
用指定颜色填充一个连接域
void cvFloodFill( CvArr* image, CvPoint seed_point, CvScalar new_val, CvScalar lo_diff=cvScalarAll(0), CvScalar up_diff=cvScalarAll(0), CvConnectedComp* comp=NULL, int flags=4, CvArr* mask=NULL ); #define CV_FLOODFILL_FIXED_RANGE (1 << 16) #define CV_FLOODFILL_MASK_ONLY (1 << 17)
函数 cvFloodFill 用指定颜色,从种子点开始填充一个连通域。连通性由象素值的接近程度来衡量。在点 (x, y) 的象素被认为是属于重新绘制的区域,如果:
其中 src(x',y') 是象素邻域点的值。也就是说,为了被加入到连通域中,一个象素的彩色/亮度应该足够接近于:
在二值图像中寻找轮廓
int cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_contour, int header_size=sizeof(CvContour), int mode=CV_RETR_LIST, int method=CV_CHAIN_APPROX_SIMPLE, CvPoint offset=cvPoint(0,0) );
函数 cvFindContours 从二值图像中提取轮廓,并且返回提取轮廓的数目。指针 first_contour 的内容由函数填写。它包含第一个最外层轮廓的指针,如果指针为 NULL,则没有检测到轮廓(比如图像是全黑的)。其它轮廓可以从 first_contour 利用 h_next 和 v_next 链接访问到。 在 cvDrawContours 的样例显示如何使用轮廓来进行连通域的检测。轮廓也可以用来做形状分析和对象识别 - 见CVPR2001 教程中的 squares 样例。该教程可以在 SourceForge 网站上找到。
初始化轮廓的扫描过程
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) );
函数 cvStartFindContours 初始化并且返回轮廓扫描器的指针。扫描器在 cvFindNextContour 使用以提取其馀的轮廓。
Finds next contour in the image
CvSeq* cvFindNextContour( CvContourScanner scanner );
函数 cvFindNextContour 确定和提取图像的下一个轮廓,并且返回它的指针。若没有更多的轮廓,则函数返回 NULL.
替换提取的轮廓
void cvSubstituteContour( CvContourScanner scanner, CvSeq* new_contour );
函数 cvSubstituteContour 把用户自定义的轮廓替换前一次的函数 cvFindNextContour 调用所提取的轮廓,该轮廓以用户定义的模式存储在边缘扫描状态之中。轮廓,根据提取状态,被插入到生成的结构,List,二层 hierarchy, 或 tree 中。如果参数 new_contour=NULL, 则提取的轮廓不被包含入生成结构中,它的所有后代以后也不会被加入到接口中。
结束扫描过程
CvSeq* cvEndFindContours( CvContourScanner* scanner );
函数 cvEndFindContours 结束扫描过程,并且返回最高层的第一个轮廓的指针。
用金字塔实现图像分割
void cvPyrSegmentation( IplImage* src, IplImage* dst, CvMemStorage* storage, CvSeq** comp, int level, double threshold1, double threshold2 );
函数 cvPyrSegmentation 实现了金字塔方法的图像分割。金字塔建立到 level 指定的最大层数。如果 p(c(a),c(b)) 定义好连接部件后,它们被加入到某些簇中。如果p(c(A),c(B)) 如果输入图像只有一个通道,那么 如果输入图像有单个通道(红、绿、兰),那幺 每一个簇可以有多个连接部件。 图像 src 和 dst 应该是 8-比特、单通道 或 3-通道图像,且大小一样 Does meanshift image segmentation The function cvPyrMeanShiftFiltering implements the filtering stage of meanshift segmentation, that is, the output of the function is the filtered "posterized" image with color gradients and fine-grain texture flattened. At every pixel (X,Y) of the input image (or down-sized input image, see below) the function executes meanshift iterations, that is, the pixel (X,Y) neighborhood in the joint space-color hyperspace is considered: {(x,y): X-sp≤x≤X+sp && Y-sp≤y≤Y+sp && ||(R,G,B)-(r,g,b)|| ≤ sr},where (R,G,B) and (r,g,b) are the vectors of color components at (X,Y) and (x,y), respectively (though, the algorithm does not depend on the color space used, so any 3-component color space can be used instead). Over the neighborhood the average spatial value (X',Y') and average color vector (R',G',B') are found and they act as the neighborhood center on the next iteration: (X,Y)~(X',Y'), (R,G,B)~(R',G',B').After the iterations over, the color components of the initial pixel (that is, the pixel from where the iterations started) are set to the final value (average color at the last iteration): I(X,Y) <- (R*,G*,B*).Then max_level>0, the gaussian pyramid of max_level+1 levels is built, and the above procedure is run on the smallest layer. After that, the results are propagated to the larger layer and the iterations are run again only on those pixels where the layer colors differ much (>sr) from the lower-resolution layer, that is, the boundaries of the color regions are clarified. Note, that the results will be actually different from the ones obtained by running the meanshift procedure on the whole original image (i.e. when max_level==0). 做分水岭图像分割 函数cvWatershed实现在[Meyer92]描述的变量分水岭,基于非参数标记的分割算法中的一种。在把图像传给函数之前,用户需要用正指标大致勾画出图像标记的感兴趣区域。比如,每一个区域都表示成一个或者多个像素值1,2,3的互联部分。这些部分将作为将来图像区域的种子。标记中所有的其他像素,他们和勾画出的区域关系不明并且应由算法定义,应当被置0。这个函数的输出则是标记区域所有像素被置为某个种子部分的值,或者在区域边界则置-1。 注:每两个相邻区域也不是必须有一个分水岭边界(-1像素)分开,例如在初始标记图像里有这样相切的部分。opencv例程文件夹里面有函数的视觉效果演示和用户例程。见watershed.cpp。 计算多边形和光栅形状的最高达三阶的所有矩 函数 cvMoments 计算最高达三阶的空间和中心矩,并且将结果存在结构 moments 中。矩用来计算形状的重心,面积,主轴和其它的形状特征,如 7 Hu 不变量等。 从矩状态结构中提取空间矩 函数 cvGetSpatialMoment 提取空间矩,当图像矩被定义为: 其中 I(x,y) 是象素点 (x, y) 的亮度值. 从矩状态结构中提取中心矩 函数 cvGetCentralMoment 提取中心矩,其中图像矩的定义是: 其中 xc=M10/M00, yc=M01/M00 - 重心坐标 从矩状态结构中提取归一化的中心矩 函数 cvGetNormalizedCentralMoment 提取归一化中心矩: 计算 7 Hu 不变量 函数 cvGetHuMoments 计算 7 个 Hu 不变量,它们的定义是: 这些值被证明为对图像缩放、旋转和反射的不变量。对反射,第7个除外,因为它的符号会因为反射而改变。 利用 Hough 变换在二值图像中找到直线 函数 cvHoughLines2 实现了用于线段检测的不同 Hough 变换方法. Example. 用 Hough transform 检测线段 这是函数所用的样本图像: 下面是程序的输出,采用概率 Hough transform ("#if 0" 的部分): 利用 Hough 变换在灰度图像中找圆 . 每个圆由三个浮点数表示:圆心坐标(x,y)和半径. Resolution of the accumulator used to detect centers of the circles. For example, if it is 1, the accumulator will have the same resolution as the input image, if it is 2 - accumulator will have twice smaller width and height, etc. Minimum distance between centers of the detected circles. If the parameter is too small, multiple neighbor circles may be falsely detected in addition to a true one. If it is too large, some circles may be missed. The first method-specific parameter. In case of CV_HOUGH_GRADIENT it is the higher threshold of the two passed to Canny edge detector (the lower one will be twice smaller). The second method-specific parameter. In case of CV_HOUGH_GRADIENT it is accumulator threshold at the center detection stage. The smaller it is, the more false circles may be detected. Circles, corresponding to the larger accumulator values, will be returned first. Minimal radius of the circles to search for. Maximal radius of the circles to search for. By default the maximal radius is set to max(image_width, image_height). The function cvHoughCircles finds circles in grayscale image using some modification of Hough transform. Example. Detecting circles with Hough transform. 计算输入图像的所有非零元素对其最近零元素的距离 函数 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] 推荐的值: 下面用户自定义距离的的距离域示例 (黑点 (0) 在白色方块中间): 用户自定义 3×3 mask (a=1, b=1.5) 用户自定义 5×5 mask (a=1, b=1.5, c=2) 典型的使用快速粗略距离估计 CV_DIST_L2, 3×3 mask , 如果要更精确的距离估计,使用 CV_DIST_L2, 5×5 mask。 When the output parameter labels is not NULL, for every non-zero pixel the function also finds the nearest connected component consisting of zero pixels. The connected components themselves are found as contours in the beginning of the function. In this mode the processing time is still O(N), where N is the number of pixels. Thus, the function provides a very fast way to compute approximate Voronoi diagram for the binary image. 修复图像中选择区域。 函数cvInpaint从选择图像区域边界的像素重建该区域。函数可以用来去除扫描相片的灰尘或者刮伤,或者从静态图像或者视频中去除不需要的物体。 多维直方图 bins : 用于存放直方图每个灰度级数目的数组指针,数组在cvCreateHist 的时候创建,其维数由cvCreateHist 确定(一般以一维比较常见) 创建直方图 函数 cvCreateHist 创建一个指定尺寸的直方图,并且返回创建的直方图的指针。 如果数组的 ranges 是 0, 则直方块的范围必须由函数 cvSetHistBinRanges 稍后指定。虽然 cvCalcHist 和 cvCalcBackProject 可以处理 8-比特图像而无需设置任何直方块的范围,但它们都被假设等分 0..255 之间的空间。 设置直方块的区间 hist ranges uniform 函数 cvSetHistBinRanges 是一个独立的函数,完成直方块的区间设置。更多详细的关于参数 ranges 和 uniform 的描述,请参考函数 cvCalcHist , 该函数也可以初始化区间。直方块的区间的设置必须在计算直方图之前,或 在计算直方图的反射图之前。 释放直方图结构 函数 cvReleaseHist 释放直方图 (头和数据). 指向直方图的指针被函数所清空。如果 *hist 指针已经为 NULL, 则函数不做任何事情。 清除直方图 函数 cvClearHist 当直方图是稠密数组时将所有直方块设置为 0,当直方图是稀疏数组时,除去所有的直方块。 从数组中创建直方图 函数 cvMakeHistHeaderForArray 初始化直方图,其中头和直方块为用户所分配。以后不需要调用 cvReleaseHist 只有稠密直方图可以采用这种方法,函数返回 hist. 查询直方块的值 宏 cvQueryHistValue_*D 返回 1D, 2D, 3D 或 N-D 直方图的指定直方块的值。对稀疏直方图,如果方块在直方图中不存在,函数返回 0, 而且不创建新的直方块。 返回直方块的指针 宏 cvGetHistValue_*D 返回 1D, 2D, 3D 或 N-D 直方图的指定直方块的指针。对稀疏直方图,函数创建一个新的直方块,且设置其为 0,除非它已经存在。 发现最大和最小直方块 函数 cvGetMinMaxHistValue 发现最大和最小直方块以及它们的位置。任何输出变量都是可选的。在具有同样值几个极值中,返回具有最小下标索引(以字母排列顺序定)的那一个。 归一化直方图 函数 cvNormalizeHist 通过缩放来归一化直方块,使得所有块的和等于 factor. 对直方图取阈值 函数 cvThreshHist 清除那些小于指定阈值得直方块 比较两个稠密直方图 函数返回 d(H1,H2) 的值。 注意:Bhattacharyya 距离只能应用到规一化后的直方图。 为了比较稀疏直方图或更一般的加权稀疏点集(译者注:直方图匹配是图像检索中的常用方法),考虑使用函数 cvCalcEMD 。 拷贝直方图 函数 cvCopyHist 对直方图作拷贝。如果第二个直方图指针 *dst 是 NULL, 则创建一个与 src 同样大小的直方图。否则,两个直方图必须大小和类型一致。然后函数将输入的直方块的值复制到输出的直方图中,并且设置取值范围与 src 的一致。 计算图像image(s) 的直方图 函数 cvCalcHist 计算一张或多张单通道图像的直方图(译者注:若要计算多通道,可像以下例子那样用多个单通道图来表示)。 用来增加直方块的数组元素可从相应输入图像的同样位置提取。 Sample. 计算和显示彩色图像的 2D 色调-饱和度图像 计算反向投影 函数 cvCalcBackProject 计算直方图的反向投影. 对于所有输入的单通道图像同一位置的象素数组,该函数根据相应的象素数组(RGB),放置其对应的直方块的值到输出图像中。用统计学术语,输出图像象素点的值是观测数组在某个分布(直方图)下的概率。 例如,为了发现图像中的红色目标,可以这么做: 这是 Camshift 彩色目标跟踪器中的一个逼进算法,除了第三步,CAMSHIFT 算法使用了上一次目标位置来定位反向投影中的目标。 用直方图比较来定位图像中的模板 函数 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 两个直方图相除 函数 cvCalcProbDensity 从两个直方图中计算目标概率密度: 所以输出的直方块小于尺度因子。 灰度图象直方图均衡化 函数 cvEqualizeHist 采用如下法则对输入图像进行直方图均衡化: 该方法归一化图像亮度和增强对比度。 例:彩色图像的直方图均衡化 在输入的源图像中与模板图像作重叠比较 函数 cvMatchTemplate 与函数 cvCalcBackProjectPatch 类似。它滑动过整个图像 image, 用指定方法比较 templ 与图像尺寸为 w×h 的重叠区域,并且将比较结果存到 result 中。 下面是不同的比较方法,可以使用其中的一种 (I 表示图像,T - 模板, R - 结果. 模板与图像重叠区域 x'=0..w-1, y'=0..h-1 之间求和): 函数完成比较后,通过使用cvMinMaxLoc找全局最小值CV_TM_SQDIFF*) 或者最大值 (CV_TM_CCORR* and CV_TM_CCOEFF*)。 比较两个形状 函数 cvMatchShapes 比较两个形状。 三个实现方法全部使用 Hu 矩 (见 cvGetHuMoments) (A ~ object1, B - object2): 其中 两个加权点集之间计算最小工作距离 函数 cvCalcEMD2 计算两个加权点集之间的移动距离或距离下界。在 [RubnerSept98] 中所描述的其中一个应用就是图像提取得多维直方图比较。 EMD 是一个使用某种单纯形算法(simplex algorithm)来解决的交通问题。其计算复杂度在最坏情况下是指数形式的,但是平均而言它的速度相当快。对实的准则,下边界的计算可以更快(使用线性时间算法),且它可用来粗略确定两个点集是否足够远以至无法联系到同一个目标上。p(c¹,c²)=|c¹-c²|.
p(c¹,c²)=0,3·(c¹r-c²r)+0,59·(c¹g-c²g)+0,11·(c¹b-c²b) .
PyrMeanShiftFiltering
void cvPyrMeanShiftFiltering( const CvArr* src, CvArr* dst,
double sp, double sr, int max_level=1,
CvTermCriteria termcrit=cvTermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,5,1));
src
termcrit
Watershed
void cvWatershed( const CvArr* image, CvArr* markers );
image
图像与轮廓矩
Moments
void cvMoments( const CvArr* arr, CvMoments* moments, int binary=0 );
arr
GetSpatialMoment
double cvGetSpatialMoment( CvMoments* moments, int x_order, int y_order );
moments
Mx_order,y_order=sumx,y(I(x,y)•xx_order•yy_order)
GetCentralMoment
double cvGetCentralMoment( CvMoments* moments, int x_order, int y_order );
moments
μx_order,y_order=sumx,y(I(x,y)•(x-xc)x_order•(y-yc)y_order),
GetNormalizedCentralMoment
double cvGetNormalizedCentralMoment( CvMoments* moments, int x_order, int y_order );
moments
ηx_order,y_order= μx_order,y_order/M00((y_order+x_order)/2+1)
GetHuMoments
void cvGetHuMoments( CvMoments* moments, CvHuMoments* hu_moments );
moments
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)²]
特殊图像变换
HoughLines
CvSeq* cvHoughLines2( CvArr* image, void* line_storage, int method,
double rho, double theta, int threshold,
double param1=0, double param2=0 );
image
rho
param2
/* 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
HoughCircles
CvSeq* cvHoughCircles( CvArr* image, void* circle_storage,
int method, double dp, double min_dist,
double param1=100, double param2=100,
int min_radius=0, int max_radius=0 );
image
method
min_dist
param1
param2
min_radius
max_radius
#include
DistTransform
void cvDistTransform( const CvArr* src, CvArr* dst, int distance_type=CV_DIST_L2,
int mask_size=3, const float* mask=NULL );
src
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
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
Inpaint
void cvInpaint( const CvArr* src, const CvArr* mask, CvArr* dst,
int flags, double inpaintRadius );
src
直方图
CvHistogram
typedef struct CvHistogram
{
int type;
CvArr* bins;
float thresh[CV_MAX_DIM][2]; /* for uniform histograms */
float** thresh2; /* for non-uniform histograms */
CvMatND mat; /* embedded matrix header for array histograms */
}
CvHistogram;
CreateHist
CvHistogram* cvCreateHist( int dims, int* sizes, int type,
float** ranges=NULL, int uniform=1 );
dims
SetHistBinRanges
void cvSetHistBinRanges( CvHistogram* hist, float** ranges, int uniform=1 );
ReleaseHist
void cvReleaseHist( CvHistogram** hist );
hist
ClearHist
void cvClearHist( CvHistogram* hist );
hist
MakeHistHeaderForArray
CvHistogram* cvMakeHistHeaderForArray( int dims, int* sizes, CvHistogram* hist,
float* data, float** ranges=NULL, int uniform=1 );
dims
QueryHistValue_1D
#define cvQueryHistValue_1D( hist, idx0 ) \
cvGetReal1D( (hist)->bins, (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
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
GetMinMaxHistValue
void cvGetMinMaxHistValue( const CvHistogram* hist,
float* min_value, float* max_value,
int* min_idx=NULL, int* max_idx=NULL );
hist
NormalizeHist
void cvNormalizeHist( CvHistogram* hist, double factor );
hist
ThreshHist
void cvThreshHist( CvHistogram* hist, double threshold );
hist
CompareHist
double cvCompareHist( const CvHistogram* hist1, const CvHistogram* hist2, int method );
hist1
d(H1,H2) =
∑
min(H1(i),H2(i))
i
CopyHist
void cvCopyHist( const CvHistogram* src, CvHistogram** dst );
src
CalcHist
void cvCalcHist( IplImage** image, CvHistogram* hist,
int accumulate=0, const CvArr* mask=NULL );
image
#include
CalcBackProject
void cvCalcBackProject( IplImage** image, CvArr* back_project, const CvHistogram* hist );
image
CalcBackProjectPatch
void cvCalcBackProjectPatch( IplImage** image, CvArr* dst,
CvSize patch_size, CvHistogram* hist,
int method, double factor );
image
CalcProbDensity
void cvCalcProbDensity( const CvHistogram* hist1, const CvHistogram* hist2,
CvHistogram* dst_hist, double scale=255 );
hist1
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)
EqualizeHist
void cvEqualizeHist( const CvArr* src, CvArr* dst );
src
int i;
IplImage *pImageChannel[4] = { 0, 0, 0, 0 };
pSrcImage = cvLoadImage( "test.jpg", 1 ) ;
IplImage *pImage = cvCreateImage(cvGetSize(pSrcImage), pSrcImage->depth, pSrcImage->nChannels);
if( pSrcImage )
{
for( i = 0; i < pSrcImage->nChannels; i++ )
{
pImageChannel[i] = cvCreateImage( cvGetSize(pSrcImage), pSrcImage->depth, 1 );
}
// 信道分离
cvSplit( pSrcImage, pImageChannel[0], pImageChannel[1],
pImageChannel[2], pImageChannel[3] );
for( i = 0; i < pImage->nChannels; i++ )
{
// 直方图均衡化
cvEqualizeHist( pImageChannel[i], pImageChannel[i] );
}
// 信道组合
cvMerge( pImageChannel[0], pImageChannel[1], pImageChannel[2],
pImageChannel[3], pImage );
// ……图像显示代码(略)
// 释放资源
for( i = 0; i < pSrcImage->nChannels; i++ )
{
if ( pImageChannel[i] )
{
cvReleaseImage( &pImageChannel[i] );
pImageChannel[i] = 0;
}
}
cvReleaseImage( &pImage );
pImage = 0;
}
匹配
MatchTemplate
void cvMatchTemplate( const CvArr* image, const CvArr* templ,
CvArr* result, int method );
image
R(x,y) =
∑
[T(x',y') − I(x + x',y + y')]2
x',y'
MatchShapes
double cvMatchShapes( const void* object1, const void* object2,
int method, double parameter=0 );
object1
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