iOS中使用OpenCV进行线性拟合(直线and曲线)
先上Github地址
线性拟合的应用领域比较广泛, 如运动轨迹计算、数据分析、图像处理等领域, 故在此写一篇学习相关文章.
直线拟合
在说直线拟合前我们先来复习一下直线方程. 直线方程的表达式有以下几种(引用自百度):
- 一般式:Ax+By+C=0(A、B不同时为0)【适用于所有直线】
A1/A2=B1/B2≠C1/C2←→两直线平行
A1/A2=B1/B2=C1/C2←→两直线重合
横截距a=-C/A
纵截距b=-C/B - 点斜式:y-y0=k(x-x0) 【适用于不垂直于x轴的直线】
表示斜率为k,且过(x0,y0)的直线 - 截距式:x/a+y/b=1【适用于不过原点或不垂直于x轴、y轴的直线】
表示与x轴、y轴相交,且x轴截距为a,y轴截距为b的直线 - 斜截式:y**=kx+b【适用于不垂直于x轴的直线】**
表示斜率为k且y轴截距为b的直线 - 两点式:【适用于不垂直于x轴、y轴的直线】
表示过(x1,y1)和(x2,y2)的直线
(y-y1)/(y2-y1)=(x-x1)/(x2-x1) (x1≠x2,y1≠y2) - 交点式:f1(x,y) *m+f2(x,y)=0 【适用于任何直线】
表示过直线f1(x,y)=0与直线f2(x,y)=0的交点的直线 - 点平式:f(x,y) -f(x0,y0)=0【适用于任何直线】
表示过点(x0,y0)且与直线f(x,y)=0平行的直线 - 法线式:x·cosα+ysinα-p=0【适用于不平行于坐标轴的直线】
过原点向直线做一条的垂线段,该垂线段所在直线的倾斜角为α,p是该线段的长度 - 点向式:(x-x0)/u=(y-y0)/v (u≠0,v≠0)【适用于任何直线】
表示过点(x0,y0)且方向向量为(u,v )的直线 - 法向式:a(x-x0)+b(y-y0)=0【适用于任何直线】****
表示过点(x0,y0)且与向量(a,b)垂直的直线
在OpenCV中使用的是点向式:(x-x0)/u=(y-y0)/v (u≠0,v≠0)
在OpenCV中我们可以主要使用fitLine函数进行直线拟合, 其算法基于最小二乘法实现的, 关于最小二乘法后续再作详细介绍. fitLine函数定义如下:
1 | void fitLine( InputArray points, OutputArray line, int distType, double param, double reps, double aeps ); |
该方法可以对二维或三维的点集进行拟合, 在二维模式下返回一个Vec4f的元素, 分别是点向式中的**(u,v,x,y)**
参数介绍:
points
输入一个二维或三维的向量, 数据结构可以是std::vector<> 或 Mat(推荐使用).
line
二维模式下返回Vec4f的元素(u,v,x,y), 三维模式下返回Vec6f的元素(vx, vy, vz, x0, y0, z0)
distType
拟合算法, 这里的拟合算法基于M-estimators实现, 有以下几种:
1 | enum DistanceTypes { |
其算法主要使用的是加权最小二乘法, 分别采用如下算法:
其中CV_DIST_L2为最简单快速的最小二乘法, 推荐使用.
param
拟合算法中的的C参, 设置为0时系统自动选用最优值.
reps
算法精度radius, 官方推荐0.01
aeps
算法精度angle, 官方推荐0.01
曲线拟合
首先,曲线的函数类型有:双曲线型、对数型、指数型、多项式型等。
对于最常见的曲线, 其特征接近于多项式型, 所以这里的曲线拟合问题就变成了多项式回归求解.
一般情况下,对实际的点数据进行求解是无解的, 所以我们需要引入最小二乘法来求解.(对于最小二乘法的原理和求解过程,后续再详细介绍)
在OpenCV中没有找到拟合曲线的算子, 这里使用C++来实现. 此算法迭代次数大于10时求解均为0. 若有更优的算法可以在评论中交流.
1 | bool fittingCurve(vector<Point2f> &vec,int times,float *p) // 输入点数据, 迭代次数, 输出多项式参数 |
下面我们来验证一组数据:
1 | int x[]={50,100,150,200,250,300,350,400,450,500,550,600,650,700,750}; |
当迭代次数为3的时候求解结果为:
1 | 381.534 |
即:y = 381.534 + 0.575073x + -0.000505627x², 图像如下:
当迭代次数为7的时候求解结果为:
1 | -210.757 |
图像如下:
以下是一些自己测试的数据: