移动最小二乘法(MLS)曲线曲面拟合C++代码实现

转自:https://blog.csdn.net/liumangmao1314/article/details/54179526

移动最小二乘法(MLS)曲线曲面拟合

曲线曲面拟合有很多种方法,Beizer,B样条等,曲面拟合移动最小二乘法是一个很好的选择,本文会详细讲解一下移动最小二乘法方法拟合曲面,并给出C++代码实现。 
本文首先是最小二乘法的分析,然后是画曲面曲线图。

目录

用 [TOC]来生成目录:


MLS的讲解

移动最小二乘法是在最小二乘法基础上加以改进的,添加了权函数等,具体的可以参考论文,“移动最小二乘法论文”链接,这篇论文对MLS讲解的很详细,最后还给出了程序设计思路。我做一点点说明,论文中的矩阵A的写法欠妥,其他关于移动最小二乘法研究中还有另外一种写法:这里写图片描述,这里的B对应论文中的P,这点要注意,这样的话A就是一个矩阵。如果是线性基的二维曲线,矩阵A就是: 
这里写图片描述,依次类推,其他的可以详看论文。

MLS代码块

代码的话我是根据论文中提供的程序设计,再结合一些网上的资料编写出来的,编程语言是C++;当然我也编写了python,应该是先编python,再编的C++。原因是python中可以加载一个矩阵运算库,C++中没有矩阵运算,要自己编写库,大家可以参考我这篇博客,介绍了矩阵运算链接。但是后来实验发现,python跑起来很费时间,C++只需它的一半的时间久跑完了,需要python代码也可以私信我,这里就不贴了。哦,对了,代码是包含很多自定义函数和变量,大家不要瞎贴代码,对照那篇论文的程序设计思路一下子就懂了,话不多说,上代码:

//移动最小二乘法的具体计算过程,参照论文“基于移动最小二乘法的曲线曲面拟合”,AB矩阵参照论文“移动最小二乘法的研究”
int MLS_Calc(int x_val,int y_val,float x[],float y[],float z[])
{
    int max_delta=max_x-min_x;//区域半径
    float p[M][N]={0};
    float sumf[N][N]={0};
    float w[M]={0};
    for(int j=0;j<M;j++)//求w
    {
        float s=fabs((x[j]-x_val))/max_delta;
        if(s<=0.5)
            w[j]=2/3.0-4*s*s+4*s*s*s;
        else
        {
            if(s<=1)
                w[j]=4/3.0-4*s+4*s*s-4*s*s*s/3.0;
            else
                w[j]=0;
        }
        p[j][0]=1;//每个采样点计算基函数
        p[j][1]=x[j];
        p[j][2]=y[j];
        p[j][3]=x[j]*x[j];
        p[j][4]=x[j]*y[j];
        p[j][5]=y[j]*y[j];
    }
    f(w,x,y,sumf,p);//计算得出A矩阵

    float p1[N];
    Matrix A=Trans_Matrix(sumf,N);
    Matrix A_1=m_c.Matrix_copy(&A);
    m_c.Matrix_inv(&A_1);//求A矩阵的逆A_1

    Matrix B(N,1);//求矩阵B,N行M列
    B.init_Matrix();
    for(int j=0;j<M;j++)//求得B矩阵的每列
    {
        p1[0]=1*w[j];
        p1[1]=x[j]*w[j];
        p1[2]=y[j]*w[j];
        p1[3]=x[j]*x[j]*w[j];
        p1[4]=x[j]*y[j]*w[j];
        p1[5]=y[j]*y[j]*w[j];
        Matrix P=Trans_Matrix_One(p1,N);//数组P1转成1行N列的P矩阵
        if(j==0)//第一列直接赋值
        {
            for(int i=0;i<N;i++)
                B.write(i,0,p1[i]);
        }
        else
        {
            m_c.Matrix_trans(&P);//矩阵转置,P转为N行1列矩阵
            m_c.Matrix_addCols(&B,&P);//矩阵B列附加,形成N行M列矩阵
        }
        P.free_Matrix();
    }

    float D[N]={1,x_val,y_val,x_val*x_val,x_val*y_val,y_val*y_val};
    Matrix D1=Trans_Matrix_One(D,N);//转成1行N列矩阵

    Matrix D_A1_mul(1,N);//定义矩阵并初始化相乘的结果矩阵,1行N列
    D_A1_mul.init_Matrix();
    if(m_c.Matrix_mul(&D1,&A_1,&D_A1_mul)==-1)
        cout<<"矩阵有误1!";//1行N列矩阵乘以N行N列矩阵得到结果为1行N列

    Matrix D_A1_B_mul(1,M);//定义矩阵并初始化相乘的结果矩阵,1行M列
    D_A1_B_mul.init_Matrix();
    if(m_c.Matrix_mul(&D_A1_mul,&B,&D_A1_B_mul)==-1)
        cout<<"矩阵有误2";//1行N列矩阵乘以N行M列矩阵得到记过矩阵为1行M列

    Matrix z1=Trans_Matrix_One(z,M);//将数组z转换成1行M列矩阵
    m_c.Matrix_trans(&z1);//转置得到M行1列矩阵
    Matrix Z(1,1);//得到矩阵结果,1行1列
    Z.init_Matrix();
    if(m_c.Matrix_mul(&D_A1_B_mul,&z1,&Z)==-1)
        cout<<"矩阵有误3!";//1行M列矩阵乘以M行1列矩阵得到1行1列矩阵,即值Z

    float z_val=Z.read(0,0);
    if(z_val>255)
        z_val=255;
    if(z_val<0)
        z_val=0;

    A.free_Matrix();
    A_1.free_Matrix();
    B.free_Matrix();

    D1.free_Matrix();
    D_A1_mul.free_Matrix();
    D_A1_B_mul.free_Matrix();
    z1.free_Matrix();
    Z.free_Matrix();

    return (int)z_val;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95

画曲线曲面

跑一个程序能看得到结果心里是很开心的,非常有成就感。想看到拟合结果,曲线C++可以画出来,具体可以参考C++画曲线链接,曲面的话matlab是不错选择,但是软件太大了,python也是可以画曲面的,网上一搜一大堆,着了就不给链接了,网上很多,参考综合。


在上一个问题中,我们已经使用Hough Circle Transform函数检测到了圆,现在我们需要使用最小二乘法拟合圆心。 最小二乘法是一种常用的拟合方法,可以用来拟合数据点到一个函数或者曲线。在这里,我们可以用最小二乘法拟合圆心。 假设我们已经检测到了n个圆,在检测到的圆中,第i个圆的圆心坐标为(xi, yi),我们需要求出这些圆心坐标的最优拟合圆(也就是最小二乘法拟合的圆)。 最小二乘法拟合圆心的思路如下: 1. 对于每个圆心(xi, yi),我们可以将它表示为(x - xi)^2 + (y - yi)^2 = r^2的形式。 2. 将上式展开,得到x^2 + y^2 - 2xi*x - 2yi*y + (xi^2 + yi^2 - r^2) = 0。 3. 对于每个圆心(xi, yi),我们可以将上式表示为Aix + Biy + Ci = Di^2的形式,其中Ai = -2xi, Bi = -2yi, Ci = xi^2 + yi^2 - r^2, Di = xi^2 + yi^2。 4. 将上述方程组表示成矩阵形式:AX = B,其中X = [a, b, c]T,A为n x 3的矩阵,B为n x 1的矩阵,X为3 x 1的矩阵,T表示矩阵的转置。 5. 使用最小二乘法求解X,即X = (ATA)^-1ATB。 6. 求出a,b,c之后,可以得到最小二乘法拟合的圆心为(x0, y0) = (-a/2, -b/2),半径为r = sqrt(a^2 + b^2 - 4c)/2。 下面是一个示例代码,演示如何使用最小二乘法拟合图像中检测到的圆心: ```c++ #include <opencv2/opencv.hpp> #include <iostream> #include <vector> using namespace std; using namespace cv; int main() { Mat image = imread("circles.png", IMREAD_GRAYSCALE); if (image.empty()) { cout << "Could not open or find the image" << endl; return -1; } Mat blurred; GaussianBlur(image, blurred, Size(5, 5), 2); vector<Vec3f> circles; HoughCircles(blurred, circles, HOUGH_GRADIENT, 1, 10, 100, 30, 5, 50); Mat A(circles.size(), 3, CV_32F); Mat B(circles.size(), 1, CV_32F); for (size_t i = 0; i < circles.size(); i++) { float xi = circles[i][0]; float yi = circles[i][1]; float ri = circles[i][2]; A.at<float>(i, 0) = -2 * xi; A.at<float>(i, 1) = -2 * yi; A.at<float>(i, 2) = xi * xi + yi * yi - ri * ri; B.at<float>(i, 0) = xi * xi + yi * yi; } Mat ATA, ATB, X; transpose(A, ATA); ATB = ATA * B; X = (ATA * A).inv() * ATB; float a = X.at<float>(0, 0); float b = X.at<float>(1, 0); float c = X.at<float>(2, 0); Point2f center(-a / 2, -b / 2); float radius = sqrt(a * a + b * b - 4 * c) / 2; // 用红色圆画出最小二乘法拟合的圆心 circle(image, center, 3, Scalar(0, 0, 255), -1, 8, 0); // 用蓝色圆画出最小二乘法拟合的圆 circle(image, center, radius, Scalar(255, 0, 0), 3, 8, 0); // 输出最小二乘法拟合的圆心和半径 cout << "Fitted Circle:" << endl; cout << "Center: (" << center.x << ", " << center.y << ")" << endl; cout << "Radius: " << radius << endl; namedWindow("Fitted Circle", WINDOW_NORMAL); imshow("Fitted Circle", image); waitKey(0); return 0; } ``` 在此示例中,我们使用前面的代码检测到了圆,并将每个圆心(xi, yi)表示为Aix + Biy + Ci = Di^2的形式。然后,我们将这些方程表示为矩阵形式,使用最小二乘法求解出拟合圆的参数a, b, c。最后,我们计算出最小二乘法拟合的圆心和半径,并在图像上画出。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值