opencv中 2D 或 3D 点集的直线拟合
代码参考:
http://hi.baidu.com/yuzaihuan/item/283d12f260513b43922af269
自己学习~
code:
#include "stdafx.h" #include "cv.h" #include "highgui.h" #include <math.h> int _tmain(int argc, _TCHAR* argv[]) { IplImage* img = cvCreateImage( cvSize( 500, 500 ), 8, 3 ); CvRNG rng = cvRNG(-1); //cvRNG()跟一般的C语言srand()使用方法一样,要先给它一个种子, //但srand()用到的是unsigned int的32位种子范围,而cvRNG()用的是64位长整数种子。 //初始化CvRNG资料结构,假如seed给0,它将会自动转成-1 cvRNG(64位种子) cvNamedWindow( "fitline", 1 ); for(;;) { char key; int i; int count = cvRandInt(&rng)%100 + 1; //产生1-100 之间的数 int outliers = count/5; // 奇异点的个数。0--20 之间的数 printf("count = %d", count); float a = cvRandReal(&rng)*200; // 0~ 199 之间的浮点数 [cvRandReal 浮点型随机数并更新 RNG ,范围在 0..1 之间,不包括 。 float b = cvRandReal(&rng)*40; //返回0 ~ 39之间的数 float angle = cvRandReal(&rng)*CV_PI; printf("count = %f", angle); float cos_a = cos(angle), sin_a = sin(angle); printf("cos_a = %f", cos_a); CvPoint pt1, pt2; //直线的两个端点 CvPoint* points = (CvPoint*)malloc( count * sizeof(points[0])); //存放随机产生的点点,数目为count CvMat pointMat = cvMat( 1, count, CV_32SC2, points ); //点集, 存储count个随机点points float line[4]; //输出的直线参数。2D 拟合情况下,它是包含 4 个浮点数的数组 (vx, vy, x0, y0) //其中 (vx, vy) 是线的单位向量而 (x0, y0) 是线上的某个点 float d, t; b = MIN(a*0.3, b); // generate some points that are close to the line for( i = 0; i < count - outliers; i++ ) { float x = (cvRandReal(&rng)*2-1)*a; float y = (cvRandReal(&rng)*2-1)*b; points[i].x = cvRound(x*cos_a - y*sin_a + img->width/2); points[i].y = cvRound(x*sin_a + y*cos_a + img->height/2); } // generate "completely off" points for( ; i < count; i++ ) { points[i].x = cvRandInt(&rng) % img->width; points[i].y = cvRandInt(&rng) % img->height; } // find the optimal line 曲线拟合 cvFitLine( &pointMat, CV_DIST_L1, 1, 0.001, 0.001, line ); cvZero( img ); //画出产生的随机分布的点点 for( i = 0; i < count; i++ ) cvCircle( img, points[i], 2, i < count - outliers ? CV_RGB(255, 0, 0) :CV_RGB(255,255,0), CV_FILLED, CV_AA, 0 ); // ... and the long enough line to cross the whole image d = sqrt((double)line[0]*line[0] + (double)line[1]*line[1]); //line[0 & 1]存储的是单位向量,所以d=1 //printf("\n %f\n", d); line[0] /= d; line[1] /= d; //画出线段的两个端点(避免线太短,以线上一个随机点向两侧延伸line[0]*t ) t = (float)(img->width + img->height) ; pt1.x = cvRound(line[2] - line[0]*t); pt1.y = cvRound(line[3] - line[1]*t); pt2.x = cvRound(line[2] + line[0]*t); pt2.y = cvRound(line[3] + line[1]*t); cvLine( img, pt1, pt2, CV_RGB(0,255,0), 3, CV_AA, 0 ); cvShowImage( "fitline", img ); key = (char) cvWaitKey(0); if( key == 27 || key == 'q' || key == 'Q' ) // 'ESC' break; free( points ); } cvDestroyWindow( "fitline" ); return 0; }
ps:
关于函数 FitLine ,参考自 opencv手册。
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