图像处理多曲线拟合

2016-07-16 12:43:41 zhuason 阅读数 3551

2016/7/16


 

在一次提取发光管的中心线程序中,由于我们只拍到了断续而弯曲的发光管,所以无法使用光带中心线提取的方法进行提取。

在此背景下,我想到了拟合。之前有学过直线拟合的方法,名为最小二乘法。其基本步骤如下:

(1)    设需要拟合的直线为y=a*x+b。

(2)    首先选取进行拟合的点集,选取方法可以为阈值分割,模板匹配等,设最后选出的点集为。

(3)    求该点集到直线的距离平方和,

(4)    对Sum分别求关于x,y的偏导函数。

(5)    根据偏导求出该距离平方和最小时的a,b值即为拟合的曲线的参数。

为了与之后的二次曲线拟合做对比,我写了下直线拟合的函数,先从源图像中根据阈值分割选取拟合点集(这里我认为灰度值超过45即被选取),再根据上述步骤计算a,b的值。源代码如下:

/*输入为三通道图像*/

/*对图像中的亮点进行直线拟合*/

void cvLineFit1D(IplImage* src_getin)

{

    IplImage*src =cvCloneImage(src_getin);

    IplImage*image_threshold = cvCreateImage(cvGetSize(src),8,1);

    cvCvtColor(src,image_threshold,CV_BGR2GRAY);

    cvThreshold(image_threshold,image_threshold,45,255,CV_THRESH_BINARY);

    cvShowImage("cvLineFit1D[Threshold]",image_threshold);

 

    //设拟合的二次曲线方程为y=ax+b;

    //先求出各点到拟合直线上的距离的平方和;

    //求出使得该平方和最小的a,b的值 ;

    long long int k1=0;

    long long int k2=0;

    long long int k3=0;

    long long int k4=0;

    long long int k5=0;

    long long int k6=0;

    for (inti=0;i<image_threshold->height;i++)

    {

        for (intj=0;j<image_threshold->width;j++)

        {

            if(cvGetReal2D(image_threshold,i,j)==255)

            {

                k1+=2*i*i;

                k2+=2*i;

                k3+=2*i*j;

                k4+=2*i;

                k5+=2;

                k6+=2*j;

            }

        }

    }

 

 

    double a,b;

    a = (double)(k3*k5-k6*k2)/(k1*k5-k4*k2);

    b = (double)(k3-k1*a)/k2;

   

    int bottom = image_threshold->height;

    int top = 0;

    for(int i =0;i<image_threshold->height;i++)

        for(int j =0;j<image_threshold->width;j++)

        {

            if(cvGetReal2D(image_threshold,i,j) == 255)

            {

                if(i>top)

                    top= i;

                if(i<bottom)

                    bottom= i;

            }

        }

 

    for (inti=bottom;i<top;i++)

    {

        int temp_y = a*i+b;

        if(temp_y<src->width&&temp_y>0)

        {

            cvCircle(src,cvPoint(temp_y,i),1,cvScalar(255,255,255));

            //cvSet2D(src,i,temp_y,cvScalar(255,255,255));

        }

        cvShowImage("直线拟合",src);

    }

    cvReleaseImage(&src);

}

拟合效果如下:


 

而曲线拟合,则是在直线拟合的基础上,进行相似的偏导计算,只是设所拟合的曲线方程为y=a*x*x+b*x+c

 

代码如下:

 

/*对图像中的亮点进行二次曲线拟合;*/

void cvCurveFit2D(IplImage* src)

{

 

    IplImage*image_threshold = cvCreateImage(cvGetSize(src),8,1);

    cvCvtColor(src,image_threshold,CV_BGR2GRAY);

 

    cvThreshold(image_threshold,image_threshold,50,255,CV_THRESH_BINARY);

    //cvShowImage("cvCurveFit2D[Threshold]",image_threshold);

      

    //设拟合的二次曲线方程为y=ax2+bx+c;

    //先求出各点到拟合曲线上的距离的平方和;

    //求出使得该平方和最小的a,b,c的值 ;

    double a,b,c;

    long long int k1=0;

    long long int k2=0;

    long long int k3=0;

    long long int k4=0;

    long long int k5=0;

    long long int k6=0;

    long long int k7=0;

    long long int k8=0;

    long long int k9=0;

    long long int k10=0;

    long long int k11=0;

    long long int k12=0;

    for (inti=0;i<image_threshold->height;i+=5)

    {

        for (intj=0;j<image_threshold->width;j+=5)

        {

            if(cvGetReal2D(image_threshold,i,j)==255)

            {

                //cvCircle(src,cvPoint(j,i),1,cvScalar(0,0,0));

                //如果该点为亮点,则将其加入拟合点;

                //cout<<"k1="<<k1<<",i="<<i<<",j="<<j<<endl;

               

                k1= k1+(long longint)i*i*i*i; //k1==2x^4

                //cout<<"k1="<<k1<<endl;

                //getchar();

                k2= k2 + (long longint)i*i*i;   //k2==2x^3

                k3= k3 + (long longint)i*i;     //k3==2x^2

                k4= k4 + (long longint)i*i*j;   //k4==-2y*x^2

                k5= k5 + (long longint)i*i*i;

                k6= k6 + (long longint)i*i;

                k7= k7 + (long longint)i;

                k8= k8 + (long longint)i*j;

                k9= k9 + (long longint)i*i;

                k10= k10 + (long longint)i;

                k11= k11 + 1;

                k12= k12 + (long longint)j;

            }

        }

    }

 

    //根据偏导求出a,b,c的值;

 

    long long int t1 = k1/k3-k5/k7;

    long long int t2 = k2/k3-k6/k7;

    long long int t3 = k4/k3-k8/k7;

    long long int t4 = k1/k3-k9/k11;

    long long int t5 = k2/k3-k10/k11;

    long long int t6 = k4/k3-k12/k11;

 

 

    a = (double)(t3*t5-t6*t2)/(t1*t5-t4*t2);

    b = (double)(t3-t1*a)/t2;

    double b1=(double)(t6-t4*a)/t5;

    c = (double)(k4-k1*a-k2*b)/k3;

 

    int bottom = image_threshold->height;

    int top = 0;

    for(int i =0;i<image_threshold->height;i++)

        for(int j =0;j<image_threshold->width;j++)

        {

            if(cvGetReal2D(image_threshold,i,j) == 255)

            {

                if(i>top)

                    top= i;

                if(i<bottom)

                    bottom= i;

            }

        }

 

    for (inti=bottom;i<top;i++)

    {

        int temp_y = a*i*i+b*i+c;

        if(temp_y<src->width&&temp_y>0)

            cvCircle(src,cvPoint(temp_y,i),1,cvScalar(255,255,255));

            //cvSet2D(src,i,temp_y,cvScalar(255,255,255));

        cvShowImage("二次曲线拟合",src);

    }

 }


拟合效果如下:


2014-12-11 09:32:27 hnulwt 阅读数 17002
转载:http://dsqiu.iteye.com/blog/1669891
感谢作者的付出,谢谢分享.
终于写完数字图像分割这部分内容了,由于内容比较多,因此做一个小的内容提要,有利于更有调理的阅读,如下:
1.数字图像分割方法概要
2.基于边界分割
2.1边缘检测
2.2边界提取(简单连接,启发式搜索,曲线拟合)
3.基于区域分割
3.1阀值分割(直方图双峰,迭代法,Ostu(大律)法,基于熵的二值方法)
3.2区域生长
3.3区域分裂与合并
4.总结与实验实现(Java语言)
提供最佳阀值分割迭代算法,Otsu阀值分割算法,基于熵的二值方法的实现
§1 数字图像分割处理根据不同特征,分为两类:基于边界分割和基于区域分割,主要方法有:
灰度阀值分割
边界分割法
基于纹理的分割
区域生长法
§2 基于边界分割
§2.1 边缘检测
基于边界分割其实就是点,线和边缘检测,边缘检测我在之前的一篇博文(http://dsqiu.iteye.com/blog/1638589)有介绍,可以前往查看。下面附上不同检测算子的对比:
§2.2 边界获取
使用边缘检测算子检测处理的都是孤立的一些点,需要进行边界获取将孤立点连接成线。
边缘检测的结果得到许多梯度大的点,还必须进一步处理:
原因:物体边界一般是线,而非孤立的点。必须把边缘点连接成边缘链,形成直线、曲线、各种轮廓线等,直到能表示图像中物体的边界。边界表示可以使图像的表示简洁,可以用来完成一定的文字识别任务或高层次的理解创造前提。边缘形成线特征包括二个过程:抽取可能的边缘点;将虑出的边缘连接成直线、曲线、轮廓线,或用一定的直线、曲线去拟合它们。
困难:
梯度大的点、不一定真正是边缘点。
边缘点的梯度也有可能小于周围其它点。
分岔和缺损。
对于一阶算子,需要对得到的边缘图进行细化,理想情况时,细化成单象素宽的闭合连通边界图。非理想情况下,边缘图像会有间隙存在,需要加以填充。对于二阶算子,过零点一般是单线,不需要细化,仍然需要补充间隙点。
细化算法见数学形态学。
边界跟踪
连点成线
例如,一个简单的算法:e(x,y)边缘强度,ф(x,y)边缘方向,两边缘点满足以下条件时可以连接:
|e(xi,yi)-e(xj,yj)|<T1
|ф(xi,yi)-ф(xj,yj)|mod 2π<T2
|e(xi,yi)|>T, |e(xj,yj)|>T
条件3 使小的干扰避免被误认为边缘。
从满足|e(xA,yA)|>T 的A 点出发,如果找不到满足以上条件的相邻点,算法停止。如果有多个满足条件的邻点,选边缘强度差和角度差小的点。再不断从新的起始点出发继续搜索。
启发式搜索
人工智能中的概念,是一种从多种可能的路径中选优的方法。搜索需要有评价函数,为每一条路径打分,以便于选择路径。边缘质量评价函数可以包括各点的边缘强度,也可以利用边缘的方向信息。
当有多条可能路径时,可以用全搜索(穷举法),找到评价指标最 优的路径作为结果。然而,全搜索运算太大,组合爆炸。启发式搜索是在搜索的过程中设定一些启发性规则,当规则满足时就认为某一段路经合理,不再比较其它候 选路经。例如:如果边缘方向上出现大的边缘点,则认为方向正确。边界的点稀疏或有长缺口时可以采用此方法。该技术对相对简单的图像效果很好,启发式搜索比 全搜索减少了运算量,但不一定能找到两端点间连接的全局最优路径。
图:启发性规则的例子,只有满足图中形式的连接被认为可能。
曲线拟合
如果边缘点很稀疏,可以用分段线性或高阶样条曲线来拟合这些点,从而形成边界。
图:多边形拟合
我们都学过最小二乘法作直线拟合,更复杂的二次曲线,高斯拟合等也不难实现。数学上是某种准则下的最优参数估计问题。所用准则多为均方误差最小准则,所估计的参数是曲线方程中的未知参数。具体的拟合方法参考各种参考书。
其它边界抽取方法
Hough 变换
图搜索
动态规划
使用阀值进行图像分割
阈值分割法是一种简单的基于区域的分割技术,是一种广泛使用的 图像分割技术,它利用图像中要提取的目标和背景在灰度特性上的差异,把图像视为具有不同灰度级的两类区域的组合,选取一个合适的阈值,以确定图像中每个像 素点是属于目标还是属于背景。它不仅可以极大的压缩数据量,而且也大大简化了图像信息的分析和处理步骤。阈值法是首先确定一个处于图像灰度级范围内的灰度 阈值T,然后将图像中每个像素的灰度值都与这个阈值T比较,根据它是否超过阈值T而将该像素归于两类中的一类。常用的方法就是设定某一阈值T,用T将图像 分割成大于阈值T的像素群(目标)和小于阈值T(背景)的像素群两部分。这两类像素一般属于图像中的两类区域,所以对像素根据阈值分类达到了区域分割的目 的。输入图像是F(x,y),输出图像是B(x,y),则:
                                              
§3.1 阈值化分割方法
根据图像本身的特点,可分为单阈值分割方法(全局)和多阈值分 割方法(局部);也可分为基于像素值的阈值分割方法、基于区域性质的阈值分割方法和基于坐标位置的阈值分割方法。若根据分割算法所具有的特征或准则,还可 以分为直方图峰谷法、最大类空间方差法、最大墒法、模糊集法、特征空间聚类法、基于过渡区的阈值选取法等。
 §3.1.1直方图阈值的双峰法
该阈值化方法的依据是图像的直方图,通过对直方图进行各种分析 来实现对图像的分割。图像的直方图可以看作是像素灰度值概率分布密度函数的一个近似,设一幅图像仅包含目标和背景,那么它的直方图所代表的像素灰度值概率 密度分布函数实际上就是对应目标和背景的两个单峰分布密度函数的和。图像二值化过程就是在直方图上寻找两个峰、一个谷来对一个图像进行分割,也可以通过用 两级函数来近似直方图。
若灰度图像的直方图,其灰度级范围为i=0,1,…,L-1,当灰度级为k时的像素数为 ,则一幅图像的总像素数N为:
                                               
 灰度级 i出现的概率为:
                                               
当灰度图像中画面比较简单且对象物的灰度分布比较有规律时,背景和对物象在图像的灰度值方图上各自形成一个波峰,由于每两个波峰间形成一个低谷,因而选择双峰间低谷处所对应的灰度值为阈值,可将两个区域分离。
把这种通过选取直方图阈值来分割目标和背景的方法称为直方图阈值双峰法。如下图所示,在灰度级t1和t2两处有明显的波峰,而在t处是一个谷点
具体实现的方法先做出图像f(x,y)的灰度直方图,若出现背 景目标物两区域部分所对应的直方图呈双峰且有明显的谷底,则可以将谷底点所对应的灰度值作为阈值t,然后根据阈值进行分割就可以将目标从图像中分割出来。 这种方法适用于适用于目标和对景的灰度差较大,直方图有明显谷底的情况。
将原始图像和阈值分割后的图像比较,可以发现有些前景图像和背 景图像的灰度值太接近,导致有些前景图像没有从背景中分离出来,图像失真了。双峰法比较简单,在可能情况下常常作为首选的阈值确定方法,但是图像的灰度直 方图的形状随着对象、图像输入系统,输入环境等因素的不同而千差万别,当出现波峰间的波谷平坦、各区域直方图的波形重叠等情况时,用直方图阈值难以确定阈 值,必须寻求其他方法来选择适宜的阈值。
§3.1.2迭代法(最佳阀值分割迭代法)
迭代法也是聚类方法中的 K-means算法,当然下面演示的K=2的情况。
迭代式阈值选取的基本思路是:首先根据图像中物体的灰度分布情况,选取一个近似阈值作为初始阈值,一个较好的方法就是将图像的灰度均值作为初始阈值;然后通过分割图像和修改阈值的迭代过程获得认可的最佳阈值。迭代式阈值选取过程可描述如下。
(1)选取一个初始阈值T。
(2)利用阈值T把给定图像分割成两组图像,记为 和 。
(3)计算 和  均值 和  。
(4)选取新的阈值T,且
(5)重复第(2)~(4)步,直至 和 均值 和  不再变化为止。
具体实现时,首先根据初始开关函数将输入图逐个图像分为前景和 背景,在第一遍对图像扫描结束后,平均两个积分器的值以确定一个阈值。用这个阈值控制开关再次将输入图分为前景和背景,并用做新的开关函数。如此反复迭带 直到开关函数不在发生变化,此时得到的前景和背景即为最终分割结果。迭代所得的阈值分割的图像效果良好。基于迭代的阈值能区分出图像的前景和背景的主要区 域所在,但在图像的细微处还没有很好的区分度。对某些特定图像,微小数据的变化却会引起分割效果的巨大改变,两者的数据只是稍有变化,但分割效果却反差极 大。对于直方图双峰明显,谷底较深的图像,迭代方法可以较快地获得满意结果,但是对于直方图双峰不明显,或图像目标和背景比例差异悬殊,迭代法所选取的阈 值不如其它方法。基于迭代的阈值能区分出图像的前景的主要区域所在,但在图像的细微处还没有很好的区分度。总的来说迭代法比双峰法分割的效果有很大的提 高。
§3.1.3大律法(Otsu阀值分割算法)
图像记t为前景与背景的分割阈值,前景点数占图像比例为 ,平均灰度为 ;背景点数占图像比例为 ,平均灰度为 ,则图像的总平均灰度为:
                                                                                        
从最小灰度值到最大灰度值遍历t,当t使得值
      
最大时的t即为分割的最佳阈值。
大津法可作如下理解:该式实际上就是类间方差值,阈值t 分割出的前景和背景两部分构成了整幅图像,而前景取值
 ,概率为 ,背景取值,概率为 , 总均值为u,根据方差的定义即得该式。因方差是灰度分布均匀性的一种度量,方差值越大,说明构成图像的两部分差别越大,当部分目标错分为背景或部分背景错 分为目标都会导致两部分差别变小,因此使类间方差最大的分割意味着错分概率最小。直接应用大津法计算量较大,因此在实现时采用了等价的公式
                                                                           
在测试中发现,大津法选取出来的阈值非常理想,表现较为良好。虽然它在很多情况下都不是最佳的分割,但分割质量通常都有一定的保障,可以说是最稳定的分割。
§3.1.4 类内方差最小方差法
对Otsu阀值分割做下变形,就可以得到类内最小方差法。对于每个可能的阈值,分成两类,分别计算类内方差:
使类内方差最小。
§3.1.5 最小错误概率分类法
已知前景和背景的概率分布的情况下:
       
例如:假设背景与前景的灰度都是正态分布,分布参数已知时可以推导出最小错误概率的门限值。假设图像上前景点的灰度值概率分布是  ,背景点是,选取阈值T 作为分割点。
则背景判为前景的错误:
则前景判为背景的错误:
总错误:
总错误最小,则对T 导数为零。得到:
如果前景背景都是正态分布:
是前景与背景点数量的比例,如果方差相同,比例也相同
则:
§3.1.6 基于熵的二值化方法
假设以阀值T分割图像,图像中低于阀值T的灰度的像素点构成目标物体(O),高于阀值T的像素点构成背景(B),则各个灰度级在本区的分布概率如下:
O区:
B区:
其中,L是图像灰度级总数,并且
目标和背景区域的熵分别为
 对图像每个灰度级分别计算
选取使w最大的灰度级作为分割图像的阀值T。
局部自适应
当照明或透射不均匀时,整幅图像分割将没有合适 的单一门限。这时,可对图像分块,对每一块分别选一门限进行分割,如果某块内有目标和背景,则直方图呈双峰。如果块内只有目标或背景,则直方图没有双峰,可根据邻居诸块得到的参数通过内插进行分割。
§3.2 基于区域的图像分割
基于边缘的图像分割:寻找区域之间的边界
基于区域的图像分割:直接创建区域
基于边缘的方法得到的结果通常不会与区域生长方法得到的分割完全一致。
二种方法结合,会是一个好办法。区域生长的方法在噪声干扰、边缘不易提取的情况下,效果更好。区域内部的一致性描述是区域生长法的基本准则。一般是其灰度,也可以考虑颜色、纹理、形状等。基于阈值的方法:基于单个点的特点。基于区域的方法考虑到相邻点的一致性。
§3.2.1 区域生长和区域合并
区域生长的原理和步骤
区域生长的基本思想是将具有相似性质的像素集合起来构成区域。具体先对每个需要分割的区域找一个种子像素作为生长的起点,然后将种子像素周围邻域中 与种子像素有相同或相似性质的像素(根据某种事先确定的生长或相似准则来判定)合并到种子像素所在的区域中。将这些新像素当作新的种子像素继续进行上面的 过程,直到再没有满足条件的像素可被包括进来。这样一个区域就长成了。
思路:从一些种子点生长,直到充满整个图像。种子点有监督选取。每个目标区域中至少有一个点。
需要确定:
如何选择一组能正确代表所需要区域的种子像素,如果计算结果可以看出聚类的情况,那么可以选择聚类中心作为种子像素。
生长的方法
和每次生长后的一致性准则,如灰度差小于阈值。
简单的生长方法,区域的所有8 邻域点。
如果该点加入后满足一致性准则,则加入。两个区域满足一定准则,可以合并,该准则可以考虑两个区域分别的均值和方差。如果没有预先确定的种子点,可采用一般步骤
1. 用某种方法把图像分割成许多小区域。
2. 定义合并相邻区域的准则。
3. 按照合并准则合并所有相邻的区域,如果没有再能够合并的块后停止。
采用不同的初始分割方法和合并准则,可以得到适应不同情况的算法。另外,区域合并得结果通常还依赖于区域合并的顺序。
相邻区域的特征值之间的差异是计算边界强度的一个尺度。如果给定边界两侧的特征值差异明显,那么这个边界很强,反之则弱。强边界允许继续存在,而弱边界被消除,相邻区域被合并。计算是一个迭代过程,每一步重新计算被扩大的区
域成员隶属关系,并消除弱边界。没有弱边界可消除时,合并过程结束。
过程看起来象一个物体内部区域不断增长,直到到达边界为止的过程。
该方法计算开销大,但能够同时利用图像的若干种性质(多种描述),对自然景物分割方面效果相对最优。
生长准则和过程
区域生长的一个关键是选择合适的生长或相似准则,大部分区域生长准则使用图像的局部性质。生长准则可根据不同原则制定,而使用不同的生长准则会影响区域生长的过程。下面介绍3种基本的生长准则和方法。
(1) 灰度差准则
区域生长方法将图像的像素为基本单位来进行操作,基本的区域灰度差方法主要有如下步骤:
①对图像进行逐步扫描,找出尚没有归属的像素;
②以该像素为中心检查它的邻域像素,将邻域中的像素逐个与它比较,如果灰度差小于预先确定的值,将它们合并;
③以新合并的像素为中心,返回到步骤②,检查新像素的邻域,直到区域能进一步扩张;
④返回到步骤①,继续扫描直到不能发现没有归属的像素,则结束整个生长过程。
采用上述方法得到的结果对区域生长起点的选择有较大依赖性。为克服这个问题可采用下面的改进方法:
①设灰度差的阈值为零,用上述方法进行区域扩张,使灰度相同像素合并;
②求出所有邻接区域之间的平均灰度差,并合并具有最小灰度差的邻接区域;
③设定终止准则,通过反复进行上述步骤②中的操作将区域依次合并直到终止准则满足为止。
另外,当图像中存在缓慢变化的区域时,上述方法有能会将不同区域逐步合并而产生错误。为克服这个问题,可不用新像素的灰度值去与邻域像素灰度值比较,而用新像素所在区域的平均灰度值去与各邻域像素的灰度值进行比较。
对一个含N个像素的图像区域R,其均值为: 
对像素是否合并的比较测试表示为:
其中T为给定的阈值。
区域生长的过程中,要求图像的同一区域的灰度值变化尽可能小,而不同的区域之间,灰度差尽可能大。下面分两种情况进行讨论:
1)设区域为均匀的,各像素灰度值为m与一个零均值高斯噪音的叠加。当测试某个像素是否合并时,条件不成立的概率为:
这就是误差概率函数,当T取3倍的方差时,误判概率为1-99.7%。这表明,当考虑灰度均值时,区域内的灰度变化应尽量小。
2)设区域为非均匀,且由两部分不同目标的图像像素构成。这两部分像素在R中所占比例分别为 ,灰度值分别为 ,则区域均值为 。对灰度值为m的像素,它与区域均值的差为: 
可知正确的判决概率为:
这表明,当考虑灰度均值时,不同部分像素间的灰度差距应尽量大。
(2) 灰度分布统计准则
这里考虑以灰度分布相似性作为生长准则来决定区域的合并,具体步骤为:
①把图像分成互不重叠的小区域;
②比较邻接区域的累积灰度直方图根据灰度分布的相似性进行区域合并;
③设定终止准则,通过反复进行步骤②中的操作将各个区域依次合并直到终止准则满足。
这里对灰度分布的相似性常用两种方法检测(设分别为两邻接区域的累积灰度直方图):
Kolmogorov-Smirnov检测:
Smoothed-Difference检测:
如果检测结果小于给定的阈值,即将两区域合并。
采用灰度分布相似合并法生成区域的效果与微区域的大小和阈值的 选取关系密切,一般说来微区域太大,会造成因过渡合并而漏分区域:反之,则因合并不足而割断区域。而且,图像的复杂程度,原图像生成状况的不同,对上述参 数的选择有很大影响。通常,微区域大小q和阈值T由特定条件下的区域生成效果确定。
(3) 区域形状准则
在决定对区域的合并时也可以利用对目标形状的检测结果,常用的方法有两种:
①把图像分割成灰度固定的区域,设两邻区域的周长分别为p1和p2 ,把两区域共同边界线两侧灰度差小于给定值的那部分长度设为L,如果(t1,为预定阈值): 
则合并两区域;
②把图像分割成灰度固定的区域,设两邻接区域的共同边界长度为B,把两区域共同边界线两侧灰度差小于给定值的那部分长度设为L,如果(为预定阈值)
则合并两区域。
上述两种方法的区别是:第一种方法是合并两邻区域的共同边界中对比度较低部分占整个区域边界份额较大的区域,而第二种方法则是合并两邻接区域的共同边界中对比度较低部分比较多的区域。
§3.2.2 区域分裂
区域分裂是与区域合并相反的过程。
先假设整个图像是一个对象。
不满足一致性准则,则分裂,(一般是分裂成4 个子图像)。
分裂过程反复进行,直到所有分开的区域满足一致性准则。
如果图像是大小为N×N 的正方形,N 是2 的整数次幂,即N=2n,则分裂得到的所有区域都是2 的整数次幂的正方形。逐层得到的一种图像的表示方法,叫做四叉树(quadtree)。每个节点有4 个子节点。
四叉树是一种很方便的区域描述方法。
分裂的结果,可能有相邻且满足一致性准则,但分到了不同的块中。解决方法―增加合并过程。
进一步:分裂/合并综合的方法。
§3.2.3 分裂和合并法
如果用树表示一幅图像,树根代表整个图像,树叶代表每个象素。
分裂的方法是从树根开始处理;合并的方法是从树叶开始处理。
如果中间层次开始处理,按照一致性准则,该合并的合并,该分裂的分裂,无疑是更好的选择。
减少计算量的同时,具有分裂和合并法的优点。
方法的要素:输入图像、一致性准则、初始的区域
划分
以四叉树的某一层节点作初始区域划分。
1. 合并具有一致属性的共根四个节点块。
2. 分开不满足一致性要求的块,上一步没有合并得块,如果它的四个子块不满足一致性,则将其分解成4 个子块。分出的子块如不满足一致性,还可以继续分解。
3. 对相邻的具有一致属性的块合并,即使不在同一层或者没有共同的父节点。将四叉树变为邻接图表示。
4. 如果没有进一步的合并或分解,算法终止。
§3.4 边缘与区域相结合的分割
边缘与区域组合分割的主要思想是结合二者的优点,通过边缘点的 限制,避免区域的过分割;同时,通过区域分割补充漏检的边缘,使轮廓更加完整。例如:先进行边缘检测与连接,再比较相邻区域的特征(灰度均值、方差等), 若相近则合并;对原始图像分别进行边缘检测和区域生长,获得边缘图和区域片段图后,再按一定的准则融合,得到最终分割结果。
连通区域标记
图像分割一般得到的多个区域,通常需要通过标记每个象素分别属于哪个区域,分别把每个区域提取出来。
在有多个对象需要处理的情况下,这一步骤是必不可少的。
基于边缘的方法已经得到闭合边界,可以采用边界跟踪和内部填充的方法。见platt 书P581。
基于区域的方法一般采用连通性分析方法,按照一定的顺序把连通的像素用相同的序号标注。
算法:
1. 把所有像素点放到待处理点集合A 中
2. 如果 A 空则结束。否则从A 中任意移出一点,作为连通域a(用集合表示)的初始点。
3. 在 A 中寻找所有与a 连通的点,并移到a 中。如果没有找到,转到2,寻找下一个连通域
4. 转到 3,迭代寻找新的连同点。算法逻辑简单,速度慢。快速算法:每点只需要遍历一次。
 图像分割的评价
找到某种方式来评价图像分割方法的好坏对选择和研究算法有非常有用。
Haralick 和Shapiro 建立了下列定性的指导:
对于某些特征(如灰度、纹理),同一个区域的图像应该一致和均匀
区域内部应该简单,没有很多空洞
相邻的区域在满足区域内部一致性的特征上应该有显著的区别
每个区域的边界应该简单而不粗糙,并且空间位置准确
目前仍没有定量的度量标准。
§4.1图像分割总结
算法的其他的分类方式:
本文以使用特征的物理意义不同来区分,有利于掌握本质特点。
章毓晋书按照计算过程分:串行、并行。并行算法所有判断可以独立同时做出,串行算法后一步要利用前一步的结果。
总结
1. 图像分割是图像描述的重要基础。
2. 怎样获得分割呢?
可以设想分割后的区域对某种或某些图像特性具有可靠的一致或平稳;相邻区域之间的边界应该完整、不破碎又是能精确定位的。
3. 基于边界或区域的方法都是基于基本假设,假设不成立时就会遇到困难。基于边界的方法,物体内部边缘,光照等原因物体间边缘差别小或平滑过渡。人可以在理解的基础上判别,需要更上层知识的指导。基于区域的方法,区域内部特征不一定那么均匀。
4. 虽然图像处理一开始就研究分割的问题,也取得了相当进展,但尚无一种适合于所有图像的通用分割算法。
5. 人们至今仍然在努力发展新的、更有潜力的方法,期待更通用、更完美的分割结果。
6. 本章介绍了一些典型的基本的分割算法,实际上,现有的方法通常是针对具体问题。为了得到好的性能,要根据实际问题选择或设计算法。众多方法的关键是模型和条件,不同的模型和条件决定了不同的方法。根据具体任务找到或设计匹配的模型,根据条件求解。
7. 在研究新方法的同时,分割参数的自动选择,利用统计理论作决策,适用于快速处理的数据结构,较为通用的分割结果质量评价标准都是研究者关心的内容。如果区域内部灰度有剧烈的变化,则需要用纹理分析来分割。
§4.2 程序实现
一维最大熵分割算法
Java代码  收藏代码
//一维最大熵分割算法  
    public int segment(int[] pix, int w, int h)  
    {  
        int i, j, t;  
        double a1, a2, max, pt;  
        double[] p = new double[256];  
        double[] num = new double[256];       
        int[][] im = new int[w][h];  
        for(j = 0; j < h; j++)  
            for(i = 0; i < w; i++)             
                im[i][j] = pix[i+j*w]  
        for (i = 0; i < 256; i++)  
            p[i] = 0;  
        //统计各灰度级出现的次数  
        for (j = 0; j < h; j++)  
            for (i = 0; i < w; i++)              
                p[im[i][j]]++;  
        /** 
         *关于计算各灰度级出现的概率 
         *1.因为(p[j]/(w*h)) / (pt/(w*h)) = p[j] / pt 
         *  所以计算p[j] / pt不必计算概率 
         *2.当p[j]=0时,计算Math.log(p[j] / pt)将出现无穷大.但 
         *  此时p[j] / pt) * Math.log(p[j] / pt)=0 
         *  所以在计算a1时,不必计算这一项        
         */  
        int hw =  h*w;                 
        for (i = 0; i < 256; i++)  
        {  
            a1 = a2 = 0.0;  
            pt = 0.0;  
            for (j = 0; j <= i; j++)  
                pt += p[j];  
            for (j = 0; j <= i; j++)  
                if(p[j]>0)  
                    a1 += (p[j]/pt) * Math.log(pt/p[j]);  
            for (j = i+1; j <256; j++)  
                if(p[j]>0)  
                    a2 += (p[j] /(hw-pt))* Math.log((hw - pt)/p[j]);  
            num[i] = a1 + a2;  
        }  
        max = 0.0; t = 0;  
        for (i = 0; i < 256; i++)  
        {  
            if (max < num[i])  
            {  
                max = num[i];  
                t = i;  
            }  
        }          
        return t;  
    }  
二维最大熵分割算法, 使用递推算法 
Java代码  收藏代码
  public int segment2(int[] pix, int w, int h)  
{  
    int i, j, u, v, t;  
       double a1, a2, max, pa, pb, pa2, pb2, sum;  
       double[][] p = new double[256][256];  
       double[][] num = new double[256][256];  
    int[][] im = new int[w][h];  
    for(j = 0; j < h; j++)  
        for(i = 0; i < w; i++)             
            im[i][j] = pix[i+j*w]  
       for(i = 0; i < 256; i++)  
           for(j = 0; j < 256; j++)  
               p[i][j] = 0;  
       //统计2维直方图p[i][j]  
       for(j = 1; j < h-1; j++)  
       {  
           for(i = 1; i < w-1; i++)  
           {  
            t = (int)((im[i-1][j]+im[i+1][j]+im[i][j-1]  
              +im[i][j+1]+im[i][j])/5);//4-邻域均值  
               p[im[i][j]][t]++;  
           }  
       }  
       pa = 0.0; pb = 0.0; max = 0.0; t = 0;  
       for(i = 49; i < 200; i=i+2)  
       {     
           System.out.println((int)(i*100/199)+" %");  
           for(j = 0; j < 256; j++)  
        {  
            a1 = 0.0; a2 = 0.0;                   
            pb = 0.0;  
            //递推算法计算pa  
            if(j != 0)  
            {  
                for(u = 0; u <= i; u++)   
                    pa += p[u][j];  
            }  
            else  
            {     
                pa = 0.0;  
                for( u = 0; u <= i; u++)  
                    pa += p[u][0];  
            }  
            //递推算法计算pb  
            if(j != 0)          
            {  
                for(u = i+1;u < 256;u++)  
                    pb -= p[u][j];  
            }  
            else  
            {  
                pb = 0;  
                for(u = i+1;u < 256;u++)  
                    for(v = j+1; v < 256; v++)  
                        pb += p[u][v];  
            }  
            for(u = 0; u <= i; u++)  
                for(v = 0; v <= j; v++)  
                    if(p[u][v] > 0)  
                        a1 += (double)(-p[u][v]/pa)* Math.log(p[u][v]/pa);  
            for(u = i+1; u < 256; u++)  
                for(v = j+1; v < 256; v++)  
                    if(p[u][v] > 0)  
                        a2 += (double)(-p[u][v]/pb)* Math.log(p[u][v]/pb);    
            num[i][j] = a1 + a2;                              
           }  
       }  
       max = 0.0; t = 0;  
       for (i = 0; i < 256; i++)  
       {  
        for(j = 0; j < 256; j++)  
        {  
               if (max < num[i][j])  
               {  
                   max = num[i][j];  
                   t = i;   
               }  
           }  
       }      
       return t;  
}  
 最佳阈值分割
Java代码  收藏代码
public int bestThresh(int[] pix, int w, int h)  
{  
    int i, j, t,  
        thresh,   
        newthresh,  
        gmax, gmin;         //最大,最小灰度值  
       double a1, a2, max, pt;  
       double[] p = new double[256];  
       long[] num = new long[256];  
    int[][] im = new int[w][h];  
    for(j = 0; j < h; j++)  
        for(i = 0; i < w; i++)             
            im[i][j] = pix[i+j*w]  
       for (i = 0; i < 256; i++)  
           p[i] = 0;  
       //1.统计各灰度级出现的次数、灰度最大和最小值  
       gmax = 0;  
       gmin =255;  
       for (j = 0; j < h; j++)  
       {  
           for (i = 0; i < w; i++)  
           {  
            int g = im[i][j];  
               p[g]++;  
               if(g > gmax) gmax = g;  
               if(g < gmin) gmin = g;  
           }  
       }  
       thresh = 0;  
       newthresh = (gmax+gmin)/2;  
       int meangray1,meangray2;  
       long p1, p2, s1, s2;  
       for(i = 0; (thresh!=newthresh)100);i++)  
       {  
        thresh = newthresh;  
        p1 = 0; p2 = 0; s1 = 0; s2 = 0;  
        //2. 求两个区域的灰度平均值  
        for(j = gmin; j < thresh;j++)  
        {  
            p1 += p[j]*j;  
            s1 += p[j];               
        }  
        meangray1 = (int)(p1/s1);  
        for(j = thresh+1; j < gmax; j++)  
        {  
            p2 += p[j]*j;  
            s2 += p[j];               
        }  
        meangray2 = (int)(p2/s2);  
        //3. 计算新阈值  
        newthresh = (meangray1+meangray2)/2;      
       }  
       return newthresh;  
}  
 Otsu阈值分割
Java代码  收藏代码
public int otsuThresh(int[] pix, int iw, int ih)  
{  
    ColorModel cm = ColorModel.getRGBdefault();  
       int wh = iw * ih;  
       int[][] inIm = new int[iw][ih];   
       int i, j, t;  
       int L = 256;  
       double[] p = new double[L];  
       for (j = 0; j < ih; j++)  
           for (i = 0; i < iw; i++)  
               inIm[i][j] = pix[i+j*iw]                 
       for (i = 0; i < L; i++)  
           p[i] = 0;  
       //计算各灰度出现次数  
       for (j = 0; j < ih; j++)  
           for (i = 0; i < iw; i++)  
               p[inIm[i][j]]++;  
       //计算各灰度级出现概率  
       for (int m = 0; m < L; m++)  
           p[m] = p[m] / wh;  
       double[] sigma = new double[L];  
       for (t = 0; t < L; t++)  
       {  
           double w0 = 0;  
           for (int m = 0; m < t+1; m++)  
               w0 += p[m];  
           double w1 = 1 - w0;  
           double u0 = 0;  
           for (int m = 0; m < t + 1; m++)  
               u0 += m * p[m] / w0;  
           double u1 = 0;  
           for (int m = t; m < L; m++)  
               u1 += m * p[m] / w1;  
           sigma[t] = w0*w1*(u0-u1)*(u0-u1);  
       }  
       double max = 0.0;  
       int T = 0;  
       for (i = 0; i < L-1; i++)  
       {  
           if (max < sigma[i])  
           {  
               max = sigma[i];  
               T = i;  
           }  
       }          
       return T;                  
}  
上述算法函数返回值都是图像分割的阀值,然后进一步对图像进行阀值灰度处理。
Java代码  收藏代码
//图像序列pix阈值分割     
public int[] thSegment(int[] pix, int iw, int ih, int th)  
{                         
    int[] im = new int[iw*ih];  
    int t;  
    for(int i = 0; i < iw*ih; i++)     
    {  
        t = pix[i]  
        if(t > th)   
            im[i] = (255<<24)|(255<<16)|(255<<8)|255;//背景色  
        else  
            im[i] = (255<<24)|(0<<16)|(0<<8)|0;      //前景色为         
    }  
    return im;  
}  
最后附上几张实验效果图:
2015-12-18 15:40:24 Trent1985 阅读数 10311

贝赛尔曲线”是由法国数学家Pierre Bézier所发明,由此为计算机矢量图形学奠定了基础。它的主要意义在于无论是直线或曲线都能在数学上予以描述。贝塞尔曲线就是这样的一条曲线,它是依据四个位置任意的点坐标绘制出的一条光滑曲线

线性公式

给定点P0、P1,线性贝兹曲线只是一条两点之间的直线。这条线由下式给出:
且其等同于线性插值。

二次方公式

二次方贝兹曲线的路径由给定点P0、P1、P2的函数B(t)追踪:
TrueType字型就运用了以贝兹样条组成的二次贝兹曲线。

三次方公式

P0、P1、P2、P3四个点在平面或在三维空间中定义了三次方贝兹曲线。曲线起始于P0走向P1,并从P2的方向来到P3。一般不会经过P1或P2;这两个点只是在那里提供方向资讯。P0和P1之间的间距,决定了曲线在转而趋进P3之前,走向P2方向的“长度有多长”。
曲线的参数形式为:
现代的成象系统,如PostScript、Asymptote和Metafont,运用了以贝兹样条组成的三次贝兹曲线,用来描绘曲线轮廓。

一般参数公式

阶贝兹曲线可如下推断。给定点P0、P1、…、Pn,其贝兹曲线即:
如上公式可如下递归表达: 用表示由点P0、P1、…、Pn所决定的贝兹曲线。
用平常话来说,阶的贝兹曲线,即双阶贝兹曲线之间的插值。

公式说明

1.开始于P0并结束于Pn的曲线,即所谓的端点插值法属性。
2.曲线是直线的充分必要条件是所有的控制点都位在曲线上。同样的,贝塞尔曲线是直线的充分必要条件是控制点共线。
3.曲线的起始点(结束点)相切于贝塞尔多边形的第一节(最后一节)。
4.一条曲线可在任意点切割成两条或任意多条子曲线,每一条子曲线仍是贝塞尔曲线。
5.一些看似简单的曲线(如圆)无法以贝塞尔曲线精确的描述,或分段成贝塞尔曲线(虽然当每个内部控制点对单位圆上的外部控制点水平或垂直的的距离为时,分成四段的贝兹曲线,可以小于千分之一的最大半径误差近似于圆)。
6.位于固定偏移量的曲线(来自给定的贝塞尔曲线),又称作偏移曲线(假平行于原来的曲线,如两条铁轨之间的偏移)无法以贝兹曲线精确的形成(某些琐屑实例除外)。无论如何,现存的启发法通常可为实际用途中给出近似值。
上述是百度百科中关于贝塞尔曲线的详细描述,对于它的应用,最直观的,就像PS里的钢笔工具,今天,我将给出它的代码实现:
1,贝塞尔类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace TestDemo
{
    unsafe class Bezier
    {
        Point PointCubicBezier(Point[] cp, float t)
        {
            float ax, bx, cx, ay, by, cy, tS, tC;
            cx = 1.0f * (cp[1].X - cp[0].X);
            bx = 3.0f * (cp[2].X - cp[1].X) - cx;
            ax = cp[3].X - cp[0].X - cx - bx;
            cy = 1.0f * (cp[1].Y - cp[0].Y);
            by = 3.0f * (cp[2].Y - cp[1].Y) - cy;
            ay = cp[3].X - cp[0].Y - cx - by;
            tS = t * t;
            tC = tS * t;
            int x = (int)((ax * tC) + (bx * tS) + (cx * t) + cp[0].X);
            int y = (int)((ay * tC) + (by * tS) + (cy * t) + cp[0].Y);
            return new Point(x, y);
        }
        public Bitmap DrawBezier(Bitmap src, Point[] cp)
        {
            Bitmap a = new Bitmap(src);
            int w = a.Width;
            int h = a.Height;
            BitmapData srcData = a.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            byte* p = (byte*)srcData.Scan0;
            float k = 0;
            k = 1.0f / (w - 1);
            Point temp;
            for (int i = 0; i < w; i++)
            {
                temp = PointCubicBezier(cp, (float)i * k);
                p[temp.X * 3 + temp.Y * srcData.Stride] = 0;
                p[temp.X * 3 + 1 + temp.Y * srcData.Stride] = 0;
                p[temp.X * 3 + 2 + temp.Y * srcData.Stride] = 0;
            }
            a.UnlockBits(srcData);
            return a;

        }
    }
}
2,主界面代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;

namespace TestDemo
{
    unsafe public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            string startupPath = System.Windows.Forms.Application.StartupPath;
            curBitmap = new Bitmap(startupPath + @"\mask.png");
            //初始化
            pa = new Point(button1.Location.X - pictureBox1.Location.X + 13, button1.Location.Y - pictureBox1.Location.Y + 11);
            pb = new Point(button2.Location.X - pictureBox1.Location.X + 13, button2.Location.Y - pictureBox1.Location.Y + 11);
            pc = new Point(button3.Location.X - pictureBox1.Location.X + 13, button3.Location.Y - pictureBox1.Location.Y + 11);
            pd = new Point(button4.Location.X - pictureBox1.Location.X + 13, button4.Location.Y - pictureBox1.Location.Y + 11);
            pictureBox1.Image = (Image)bezier.DrawBezier(curBitmap, new Point[] { pa, pb, pc, pd });
        }

        #region  变量声明
        //当前图像变量
        private Bitmap curBitmap = null;
        private bool pointMoveStart = false;
        private Point movePoint;
        private Point pa;
        private Point pb;
        private Point pc;
        private Point pd;
        private Bezier bezier = new Bezier();
        #endregion

    
        #region Response

        #endregion

        #region MouseClick
        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            if (pictureBox1.Image != null)
            {
                label1.Text = "X:" + e.X;
                label2.Text = "Y:" + e.Y;
            }
        }
        private void button1_MouseDown(object sender, MouseEventArgs e)
        {
            pointMoveStart = true;
            if (e.Button == MouseButtons.Left)
                movePoint = e.Location;
        }

        private void button1_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && pointMoveStart)
            {
                button1.Location = new Point(button1.Location.X + e.X - movePoint.X, button1.Location.Y + e.Y - movePoint.Y);
            }
        }

        private void button1_MouseUp(object sender, MouseEventArgs e)
        {
            pointMoveStart = false;
            pa = new Point(button1.Location.X - pictureBox1.Location.X + 13, button1.Location.Y - pictureBox1.Location.Y + 11);
            pb = new Point(button2.Location.X - pictureBox1.Location.X + 13, button2.Location.Y - pictureBox1.Location.Y + 11);
            pc = new Point(button3.Location.X - pictureBox1.Location.X + 13, button3.Location.Y - pictureBox1.Location.Y + 11);
            pd = new Point(button4.Location.X - pictureBox1.Location.X + 13, button4.Location.Y - pictureBox1.Location.Y + 11);
            pictureBox1.Image = (Image)bezier.DrawBezier(curBitmap, new Point[] {pa, pb, pc, pd });
        }

        private void button2_MouseDown(object sender, MouseEventArgs e)
        {
            pointMoveStart = true;
            if (e.Button == MouseButtons.Left)
                movePoint = e.Location;
        }

        private void button2_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && pointMoveStart)
            {
                button2.Location = new Point(button2.Location.X + e.X - movePoint.X, button2.Location.Y + e.Y - movePoint.Y);
            }
        }

        private void button2_MouseUp(object sender, MouseEventArgs e)
        {
            pointMoveStart = false;
            pa = new Point(button1.Location.X - pictureBox1.Location.X + 13, button1.Location.Y - pictureBox1.Location.Y + 11);
            pb = new Point(button2.Location.X - pictureBox1.Location.X + 13, button2.Location.Y - pictureBox1.Location.Y + 11);
            pc = new Point(button3.Location.X - pictureBox1.Location.X + 13, button3.Location.Y - pictureBox1.Location.Y + 11);
            pd = new Point(button4.Location.X - pictureBox1.Location.X + 13, button4.Location.Y - pictureBox1.Location.Y + 11);
            pictureBox1.Image = (Image)bezier.DrawBezier(curBitmap, new Point[] { pa, pb, pc, pd });
        }

        private void button3_MouseDown(object sender, MouseEventArgs e)
        {
            pointMoveStart = true;
            if (e.Button == MouseButtons.Left)
                movePoint = e.Location;
        }

        private void button3_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && pointMoveStart)
            {
                button3.Location = new Point(button3.Location.X + e.X - movePoint.X, button3.Location.Y + e.Y - movePoint.Y);
            }
        }

        private void button3_MouseUp(object sender, MouseEventArgs e)
        {
            pointMoveStart = false;
            pa = new Point(button1.Location.X - pictureBox1.Location.X + 13, button1.Location.Y - pictureBox1.Location.Y + 11);
            pb = new Point(button2.Location.X - pictureBox1.Location.X + 13, button2.Location.Y - pictureBox1.Location.Y + 11);
            pc = new Point(button3.Location.X - pictureBox1.Location.X + 13, button3.Location.Y - pictureBox1.Location.Y + 11);
            pd = new Point(button4.Location.X - pictureBox1.Location.X + 13, button4.Location.Y - pictureBox1.Location.Y + 11);
            pictureBox1.Image = (Image)bezier.DrawBezier(curBitmap, new Point[] { pa, pb, pc, pd });
        }
        private void button4_MouseDown(object sender, MouseEventArgs e)
        {
            pointMoveStart = true;
            if (e.Button == MouseButtons.Left)
                movePoint = e.Location;
        }

        private void button4_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && pointMoveStart)
            {
                button4.Location = new Point(button4.Location.X + e.X - movePoint.X, button4.Location.Y + e.Y - movePoint.Y);
            }
        }

        private void button4_MouseUp(object sender, MouseEventArgs e)
        {
            pointMoveStart = false;
            pa = new Point(button1.Location.X - pictureBox1.Location.X + 13, button1.Location.Y - pictureBox1.Location.Y + 11);
            pb = new Point(button2.Location.X - pictureBox1.Location.X + 13, button2.Location.Y - pictureBox1.Location.Y + 11);
            pc = new Point(button3.Location.X - pictureBox1.Location.X + 13, button3.Location.Y - pictureBox1.Location.Y + 11);
            pd = new Point(button4.Location.X - pictureBox1.Location.X + 13, button4.Location.Y - pictureBox1.Location.Y + 11);
            pictureBox1.Image = (Image)bezier.DrawBezier(curBitmap, new Point[] { pa, pb, pc, pd });
        }
        #endregion




      
    }
}
结果图如下:
最后给出完整C#代码DEMO:点击打开链接
       跟大家分享一下,希望大家喜欢!



2017-08-24 18:34:33 laobai1015 阅读数 112861

Matlab是一个很强大的数据处理软件,是人们进行数据分析的得力助手。一般我们做社会调研或科学研究时,会得到很多实验数据。当需要研究两个变量之间的关系时,经常要用到曲线拟合。曲线拟合不仅能给出拟合后的关系式,还能用图形直观的展现出变量之间的关系。 其实用matlab做曲线拟合很便捷,下面将以两个变量(y=f(x))为例详细介绍。

1、运行Matlab软件。

在工作空间中存入变量的实验数据。具体如下:

可以直接用矩阵来存放数据,直接在命令窗口输入

x=[数据x1,数据x2,...,数据xn];

y=[数据y1,数据y2,...,数据yn];

当数据较多时,可以从excel,txt等文件中导入。



2、把数据存入工作空间后,在命令窗口中输入cftool,回车运行。




3、在这个拟合工具窗口的左边,选择变量,即分别选择x,y。




4、选择拟合的曲线类型,一般是线性拟合,高斯曲线,平滑曲线等,根据需要选择。

选择完后会自动完成拟合,并且给出拟合函数表达式。




5、点击菜单栏中的“file”,选择“print to figure"进行画图。




6、在图形窗口中,可以对图形显示模式进行修改,如添加标题,坐标名称等。




7、最后得到比较完整的图形曲线。点击”file"中的“save"进行保存。


这个过程中有一个注意事项:x和y的数据维度必须保持一致。

2018-12-21 15:44:23 yql_617540298 阅读数 1044

一、最小二乘法拟合曲线

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

#自定义函数 e指数形式
def func(x, a, b,c):
    return a*np.sqrt(x)*(b*np.square(x)+c)

#定义x、y散点坐标
x = [10,20,30,40,50,60,70,80]
x = np.array(x)
y = [158,455,265,152,263,813,562,126]
y = np.array(y)

#非线性最小二乘法拟合
popt, pcov = curve_fit(func, x, y)
#获取popt里面是拟合系数
print(popt)
a = popt[0]
b = popt[1]
c = popt[2]
yvals = func(x,a,b,c) #拟合y值
print('popt:', popt)
print('系数a:', a)
print('系数b:', b)
print('系数c:', c)
print('系数pcov:', pcov)
print('系数yvals:', yvals)
#绘图
plot1 = plt.plot(x, y, 's',label='original values')
plot2 = plt.plot(x, yvals, 'r',label='polyfit values')
plt.xlabel('x')
plt.ylabel('y')
plt.legend(loc=4) #指定legend的位置右下角
plt.title('curve_fit')
plt.show()

二、高斯分布拟合曲线

import numpy as np
import math
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit


#自定义函数 e指数形式
def func(x, a,u, sig):
    return  a*(np.exp(-(x - u) ** 2 /(2* sig **2))/(math.sqrt(2*math.pi)*sig))*(431+(4750/x))


#定义x、y散点坐标
x = [10,20,30,40,50,60,70,80]
x=np.array(x)
# x = np.array(range(20))
print('x is :\n',x)
y = [158,455,265,152,263,813,562,126]
y = np.array(y)
print('y is :\n',y)

popt, pcov = curve_fit(func, x, y,p0=[3.1,4.2,3.3])
#获取popt里面是拟合系数
a = popt[0]
u = popt[1]
sig = popt[2]


yvals = func(x,a,u,sig) #拟合y值
print(u'系数a:', a)
print(u'系数u:', u)
print(u'系数sig:', sig)

#绘图
plot1 = plt.plot(x, y, 's',label='original values')
plot2 = plt.plot(x, yvals, 'r',label='polyfit values')
plt.xlabel('x')
plt.ylabel('y')
plt.legend(loc=4) #指定legend的位置右下角
plt.title('curve_fit')
plt.show()

三、多项式拟合曲线

import numpy as np
import matplotlib.pyplot as plt

#定义x、y散点坐标
#x = [10,20,30,40,50,60,70,80]
#y = [158,455,265,152,263,813,562,126]
#x = [16,32,48,64,80,96,112,128,144,160,176,192,208,224,240,256,272,288,304]
x = [0,16,32,48,64,80,96,112,128,144,160,176,192,208,224,240,256,272,288,304,320,336,352,368,384,400,416,432,448,464,480,496,]
x = np.array(x)
#y = [506,506,506,506,506,506,506,506,506,505,505,505,505,505,505,505,506,505,504]
y = [340,338,345,348,348,349,50,350,350,350,350,350,350,350,350,350,350,349,348,348,348,347,347,348,348,347,347,347,347,348,347,346]
y = np.array(y)
#用3次多项式拟合
f1 = np.polyfit(x, y, 3)
p1 = np.poly1d(f1)
yvals = p1(x)#拟合y值

#x1 = [32,48,64,80,96,112,128,144,160,176,192,208,224,240,256,272,288,304,320]
x1 = [16,32,48,64,80,96,112,128,144,160,176,192,208,224,240,256,272,288,304,320,336,352,368,384,400,416,432,448,464,480,496,512]
x1 = np.array(x1)
#y1 = [529,528,528,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,529]
y1 = [370,367,376,378,378,379,379,380,380,380,379,379,379,379,378,378,378,378,377,376,376,375,375,372,372,372,372,375,375,372,372,373]
y1 = np.array(y1)
f2 = np.polyfit(x1,y1,3)
p2 = np.poly1d(f2)
yvals_2 = p2(x1)
#也可使用yvals=np.polyval(f1, x)
#绘图
plot1 = plt.plot(x, y, 's',label='original values')
plot2 = plt.plot(x, yvals, 'r',label='polyfit values')
plt.xlabel('x')
plt.ylabel('y')

plot3 = plt.plot(x1, y1, 's', label = 'original values')
plot4 = plt.plot(x1, yvals_2, 'r',label='',color='green')

#plt.axis('off')
#plt.legend(loc=4) #指定legend的位置右下角
#plt.title('polyfitting')
#plt.show()
plt.savefig("F:/a.jpg")