B样条基函数——B-Spline Basis Functions

B-Spline Basis Functions

[email protected]

    摘要Abstract:直接根据B样条的Cox-deBoor递推定义写出计算B样条基函数的程序,并将计算结果在OpenSceneGraph中显示。

   关键字Key Words:B Spline Basis Functions、OpenSceneGraph

一、概述Overview

有很多方法可以用来定义B样条基函数以及证明它的一些重要性质。例如,可以采用截尾幂函数的差商定义,开花定义,以及由de Boor和Cox等人提出的递推公式等来定义。我们这里采用的是递推定义方法,因为这种方法在计算机实现中是最有效的。

令U={u0,u1,…,um}是一个单调不减的实数序列,即ui<=ui+1,i=0,1,…,m-1。其中,ui称为节点,U称为节点矢量,用Ni,p(u)表示第i个p次B样条基函数,其定义为:

B样条基有如下性质:

a) 递推性;

b) 局部支承性;

c) 规范性;

d) 可微性;

二、程序 Codes

直接根据B样条基函数的Cox-deBoor递推定义,写出计算B样条基函数的程序如下:

头文件BSplineBasisFunction.h:

 

/**//*
*    Copyright (c) 2013 eryar All Rights Reserved.
*
*        File    : BSplineBasisFunction.h
*        Author  : [email protected]
*        Date    : 2013-03-23 22:13
*        Version : V1.0
*
*    Description : Use Cox-deBoor formula to implemente the 
*                  B-Spline Basis functions.
*
*/
#ifndef _BSPLINEBASISFUNCTION_H_
#define _BSPLINEBASISFUNCTION_H_

#include <vector>

class BSplineBasisFunction
{
public:
    BSplineBasisFunction(const std::vector<double>& U);
    ~BSplineBasisFunction(void);

public:
    /**//*
    * @brief Binary search of the knot vector.
    */
    int FindSpan(double u);

    /**//*
    * @brief 
    * @param [in] i: span of the parameter u;
    *        [in] p: degree;
    *        [in] u: parameter;
    */
    double EvalBasis(int i, int p, double u);

    /**//*
    * @breif Get knot vector size.
    */
    int GetKnotVectorSize(void) const;

    /**//*
    * @breif Get the knot value of the given index.
    */
    double GetKnot(int i) const;

private:
    std::vector<double> mKnotVector;
};

#endif // _BSPLINEBASISFUNCTION_H_


实现文件BSplineBasisFunction.cpp:

 

/**//*
*    Copyright (c) 2013 eryar All Rights Reserved.
*
*        File    : BSplineBasisFunction.cpp
*        Author  : [email protected]
*        Date    : 2013-03-23 22:14
*        Version : V1.0
*
*    Description : Use Cox-deBoor formula to implemente the 
*                  B-Spline Basis functions.
*
*/

#include "BSplineBasisFunction.h"

BSplineBasisFunction::BSplineBasisFunction( const std::vector<double>& U )
    :mKnotVector(U)
{

}


BSplineBasisFunction::~BSplineBasisFunction(void)
{
}

int BSplineBasisFunction::GetKnotVectorSize( void ) const
{
    return static_cast<int> (mKnotVector.size());
}

double BSplineBasisFunction::GetKnot( int i ) const
{
    return mKnotVector[i];
}

/**//*
* @brief Binary search of the knot vector.
*/
int BSplineBasisFunction::FindSpan( double u )
{
    int iSize = static_cast<int> (mKnotVector.size());

    if (u >= mKnotVector[iSize-1])
    {
        return iSize;
    }

    int iLow = 0;
    int iHigh = iSize;
    int iMiddle = (iLow + iHigh) / 2;

    while (u < mKnotVector[iMiddle] || u > mKnotVector[iMiddle+1])
    {
        if (u < mKnotVector[iMiddle])
        {
            iHigh = iMiddle;
        }
        else
        {
            iLow = iMiddle;
        }

        iMiddle = (iLow + iHigh) / 2;
    }

    return iMiddle;
}

double BSplineBasisFunction::EvalBasis( int i, int p, double u )
{
    if ((i+p+1) >= GetKnotVectorSize())
    {
        return 0;
    }

    if (0 == p)
    {
        if (u >= mKnotVector[i] && u < mKnotVector[i+1])
        {
            return 1;
        } 
        else
        {
            return 0;
        }
    }

    double dLeftUpper = u - mKnotVector[i];
    double dLeftLower = mKnotVector[i+p] - mKnotVector[i];
    double dLeftValue = 0;

    double dRightUpper = mKnotVector[i+p+1] - u;
    double dRightLower = mKnotVector[i+p+1] - mKnotVector[i+1];
    double dRightValue = 0;

    if (dLeftUpper != 0 && dLeftLower != 0)
    {
        dLeftValue = (dLeftUpper / dLeftLower) * EvalBasis(i, p-1, u);
    }

    if (dRightUpper != 0 && dRightLower != 0)
    {
        dRightValue = (dRightUpper / dRightLower) * EvalBasis(i+1, p-1, u);
    }

    return (dLeftValue + dRightValue);
}

主函数:

 

/**//*
*    Copyright (c) 2013 eryar All Rights Reserved.
*
*        File    : Main.cpp
*        Author  : [email protected]
*        Date    : 2013-03-23 22:11
*        Version : V1.0
*
*    Description : Use Cox-deBoor formula to implemente the 
*                  B-Spline Basis functions.
*
*/

#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osgGA/StateSetManipulator>
#include <osgViewer/ViewerEventHandlers>

#include "BSplineBasisFunction.h"

#pragma comment(lib, "osgd.lib")
#pragma comment(lib, "osgDBd.lib")
#pragma comment(lib, "osgGAd.lib")
#pragma comment(lib, "osgViewerd.lib")

osg::Node* MakeBasisFuncLine(BSplineBasisFunction& bf, int i, int p)
{
    // The property basis functions.
    int iLen = bf.GetKnotVectorSize();
    int iStep = 800;
    double dStart = bf.GetKnot(0);
    double dEnd = bf.GetKnot(iLen-1);
    double dDelta = (dEnd - dStart) / iStep;
    double u = 0;
    double v = 0;

    // Create the Geode (Geometry Node) to contain all our osg::Geometry objects.
    osg::Geode* geode = new osg::Geode;

    // Create Geometry object to store all the vertices and lines primitive.
    osg::ref_ptr<osg::Geometry> linesGeom = new osg::Geometry;

    // Set the vertex array to the points geometry object.
    osg::ref_ptr<osg::Vec3Array> pointsVec = new osg::Vec3Array;

    for (int s = 0; s <= iStep; s++)
    {
        u = s * dDelta;
        v = bf.EvalBasis(i, p, u);
        if (v != 0)
        {
            pointsVec->push_back(osg::Vec3(u, 0, v));
        }
    }
    linesGeom->setVertexArray(pointsVec);

    // Set the colors.
    osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;
    colors->push_back(osg::Vec4(1.0f, 1.0f, 0.0f, 0.0f));
    linesGeom->setColorArray(colors.get());
    linesGeom->setColorBinding(osg::Geometry::BIND_OVERALL);

    // Set the normal in the same way of color.
    osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;
    normals->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));
    linesGeom->setNormalArray(normals.get());
    linesGeom->setNormalBinding(osg::Geometry::BIND_OVERALL);

    // Add the points geometry to the geode.
    linesGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, pointsVec->size()));
    geode->addDrawable(linesGeom.get());

    return geode;
}

osg::Node* CreateScene(void)
{
    osg::Group* root = new osg::Group;

    // Knot vector: U={0,0,0,1,2,3,4,4,5,5,5}.
    std::vector<double> knotVector;
    knotVector.push_back(0);
    knotVector.push_back(0);
    knotVector.push_back(0);
    knotVector.push_back(1);
    knotVector.push_back(2);
    knotVector.push_back(3);
    knotVector.push_back(4);
    knotVector.push_back(4);
    knotVector.push_back(5);
    knotVector.push_back(5);
    knotVector.push_back(5);

    BSplineBasisFunction basisFunc(knotVector);
 
    for (int i = 0; i < basisFunc.GetKnotVectorSize(); i++)
    {
        // 
        //root->addChild(MakeBasisFuncLine(basisFunc, i, 1));

        // 
        root->addChild(MakeBasisFuncLine(basisFunc, i, 2));
    }
    
    return root;
}

int main(int argc, char* argv[])
{
    osgViewer::Viewer viewer;
    viewer.setSceneData(CreateScene());

    viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
    viewer.addEventHandler(new osgViewer::StatsHandler);
    viewer.addEventHandler(new osgViewer::WindowSizeHandler);

    return viewer.run();
}

 

若想显示出所有次数的B样条基函数,只需要在CreateScene中添加就好了。

以《The NURBS Book》中的例子2.2,节点矢量U={0, 0, 0, 1, 2, 3, 4, 4, 5, 5, 5},次数p=2,分别将程序计算的一次、二次B样条基函数的结果列出,如下图所示:

图1. 一次B样条基函数

图2. 二次B样条基函数

本来还想将不同的B样条基函数以不同的颜色显示,试了几次,都没有成功。若以不同的颜色显示,会更直观。若你有设置颜色的方法,欢迎告诉我,[email protected]

三、结论 Conclusion

程序计算结果与书中吻合,效果还不错。

理解了B样条的Cox-deBoor递推定义之后,可以将程序中的递归代码转换为非递归实现,这样就可以深入理解B样条基函数了。

 

PDF Version and Codes: B-Spline Basis Functions

时间: 2013-03-27
Tags: 函数

B样条基函数&mdash;&mdash;B-Spline Basis Functions的相关文章

Linux监控工具介绍系列&amp;mdash;&amp;mdash;OSWatcher Black Box

  OSWatcher Balck Box简介   OSWatcher Black Box (oswbb)是Oracle开发.提供的一个小巧,但是实用.强大的系统工具,它可以用来抓取操作系统的性能指标,用于辅助监控系统的资源使用.其安装部署.卸载都非常简单:资源消耗也比较小,原理也十分简单,它通过调用OS的的一些命令(例如vmstat.iostat等)来采集.存储CPU/Memory/Swap/Disk IO/Nentwork相关数据.安装和运行oswbba可以帮助在性能诊断时提供丰富多样的各类

MS SQL巡检系列&amp;mdash;&amp;mdash;检查外键字段是否缺少索引

前言感想:一时兴起,突然想写一个关于MS SQL的巡检系列方面的文章,因为我觉得这方面的知识分享是有价值,也是非常有意义的.一方面,很多经验不足的人,对于巡检有点茫然,不知道要从哪些方面巡检,另外一方面,网上关于MS SQL巡检方面的资料好像也不是特别多.写这个系列只是一个分享,自己的初衷是一个知识梳理.总结提炼过程,有些知识和脚本也不是原创,文章很多地方融入了自己的一些想法和见解的,不足和肤浅之处肯定也非常多,抛砖引玉,也希望大家提意见和建议.补充,指正其中的不足之处.Stay Hungry

MySQL备份还原&amp;mdash;&amp;mdash;mysqldump工具介绍

mysqldump是一款MySQL逻辑备份的工具,他将数据库里面的对象(表)导出成SQL脚本文件.有点类似于SQL SEVER的"任务-生成脚本"的逻辑备份功能.mysqldump是比较常用的备份方法,适合于在不同MySQL版本之间的迁移.升级.不足之处就是数据库比较大时,mysqldump的效率不高.   mysqldump的主要参数收集整理注释如下,如果你要查看mysqldump的具体参数信息,可以使用命令mysqldump –help   --add-drop-table    

Linux监控工具介绍系列&amp;mdash;&amp;mdash;smem

smem工具介绍         smem是Linux系统上的一款可以生成多种内存耗用报告的命令行工具.与现有工具不一样的是smem可以报告实际使用的物理内存(PSS),这是一种更有意义的指标.可以衡量虚拟内存系统的库和应用程序所占用的内存数量. 由于大部分的物理内存通常在多个应用程序之间共享,名为实际使用物理内存(RSS)的这个标准的内存耗用衡量指标会大大高估内存耗用情况.PSS这个参数而是衡量了每个应用程序在每个共享内存区中的"公平分配",给出了一个切合实际的衡量指标.   sme

VS开发中的代码编写小技巧&amp;mdash;&amp;mdash;避免重复代码编写的几种方法

原文:VS开发中的代码编写小技巧--避免重复代码编写的几种方法 上一篇文章中程序员的幸福生活--有你的日子,每天都是情人节,收到了大家的很多好评.鼓励和祝福,非常感动,真诚的谢谢大家.也希望每个朋友都能保持一个积极向上的心态,去迎接丰富多彩的人生. 在开发过程中,我们经常会遇到大量重复或者类似的代码需要编写,当然我们可以通过各种模式来避免这种情况出现,但肯定有些时候我们是无法避免的,那么遇到这种情况,我们该如何快速完成这些重复或类似的代码的编写呢.下面来说一下我的方法,当然,如果大家有更好的方法

Linux LVM学习总结&amp;mdash;&amp;mdash;创建卷组VG

    在Linux平台如何创建一个卷组(VG)呢?下面简单介绍一下卷组(VG)的创建步骤.本文实验平台为Red Hat Enterprise Linux Server release 6.6 (Santiago) 1: 查看当前卷组信息 查看服务器的卷组(VG).物理卷(PV).逻辑卷(LV)相关信息,在此之前,我们先来看看一些基本命令以及用法     vgcan命令介绍 :     查找系统中存在的LVM卷组,并显示找到的卷组列表.vgscan命令仅显示找到的卷组的名称和LVM元数据类型,要

Linux LVM学习总结&amp;mdash;&amp;mdash;删除物理卷

本篇介绍LVM管理中的命令vgreduce, pvremove.其实前面几篇中以及有所涉及.    vgreduce:通过删除LVM卷组中的物理卷来减少卷组容量.注意:不能删除LVM卷组中剩余的最后一个物理卷    pvremove: 用于删除一个存在的物理卷.使用pvremove指令删除物理卷时,它将LVM分区上的物理卷信息删除,使其不再被视为一个物理卷. 在删除物理卷PV之前,必须将服务器的分区信息.卷组信息.物理卷信息.逻辑卷信息都掌握清楚,避免出现失误或误操作. [[email protected]

玩转Windows服务系列&amp;mdash;&amp;mdash;Windows服务小技巧

原文:玩转Windows服务系列--Windows服务小技巧 伴随着研究Windows服务,逐渐掌握了一些小技巧,现在与大家分享一下. 将Windows服务转变为控制台程序 由于默认的Windows服务程序,编译后为Win32的窗口程序.我们在程序启动或运行过程中,如果想看到一些调试信息,那么就只能通过DebugView或者输出到日志的方式了.因为如果我们通过printf或者std::cout输出调试信息的话,Win32窗口程序是无法显示的. 此时,我们是多么怀念我们的经典的控制台程序啊,它可以

MySQL备份还原&amp;mdash;&amp;mdash;AutoMySQLBackup介绍

AutoMySQLBackup是一个开源的MySQL备份脚本.可以说它是一个轻量级的备份方案,AutoMySQLBackup的安装.配置非常简单.方便.AutoMySQLBackup的sourceforge上介绍有如它本身,也非常的简单: Description AutoMySQLBackup with a basic configuration will create Daily, Weekly and Monthly backups of one or more of your MySQL