[CC]平面拟合
常见的平面拟合方法一般是最小二乘法。当误差服从正态分布时,最小二乘方法的拟合效果还是很好的,可以转化成PCA问题。
当观测值的误差大于2倍中误差时,认为误差较大。采用最小二乘拟合时精度降低,不够稳健。
提出了一些稳健的方法:有移动最小二乘法(根据距离残差增加权重);采用2倍距离残差的协方差剔除离群点;迭代重权重方法(选权迭代法)。
MainWindow中的平面拟合方法,调用了ccPlane的Fit方法。
void MainWindow::doActionFitPlane()
{
doComputePlaneOrientation(false);
} void MainWindow::doActionFitFacet()
{
doComputePlaneOrientation(true);
} static double s_polygonMaxEdgeLength = ;
void MainWindow::doComputePlaneOrientation(bool fitFacet)
{
ccHObject::Container selectedEntities = m_selectedEntities;
size_t selNum = selectedEntities.size();
if (selNum < )
return; double maxEdgeLength = ;
if (fitFacet)
{
bool ok = true;
maxEdgeLength = QInputDialog::getDouble(this,"Fit facet", "Max edge length (0 = no limit)", s_polygonMaxEdgeLength, , 1.0e9, , &ok);
if (!ok)
return;
s_polygonMaxEdgeLength = maxEdgeLength;
} for (size_t i=; i<selNum; ++i)
{
ccHObject* ent = selectedEntities[i];
ccShiftedObject* shifted = ;
CCLib::GenericIndexedCloudPersist* cloud = ; if (ent->isKindOf(CC_TYPES::POLY_LINE))
{
ccPolyline* poly = ccHObjectCaster::ToPolyline(ent);
cloud = static_cast<CCLib::GenericIndexedCloudPersist*>(poly);
shifted = poly;
}
else
{
ccGenericPointCloud* gencloud = ccHObjectCaster::ToGenericPointCloud(ent);
if (gencloud)
{
cloud = static_cast<CCLib::GenericIndexedCloudPersist*>(gencloud);
shifted = gencloud;
}
} if (cloud)
{
double rms = 0.0;
CCVector3 C,N; ccHObject* plane = ;
if (fitFacet)
{
ccFacet* facet = ccFacet::Create(cloud, static_cast<PointCoordinateType>(maxEdgeLength));
if (facet)
{
plane = static_cast<ccHObject*>(facet);
N = facet->getNormal();
C = facet->getCenter();
rms = facet->getRMS(); //manually copy shift & scale info!
if (shifted)
{
ccPolyline* contour = facet->getContour();
if (contour)
{
contour->setGlobalScale(shifted->getGlobalScale());
contour->setGlobalShift(shifted->getGlobalShift());
}
}
}
}
else
{
ccPlane* pPlane = ccPlane::Fit(cloud, &rms);
if (pPlane)
{
plane = static_cast<ccHObject*>(pPlane);
N = pPlane->getNormal();
C = *CCLib::Neighbourhood(cloud).getGravityCenter();
pPlane->enableStippling(true);
}
} //as all information appears in Console...
forceConsoleDisplay(); if (plane)
{
ccConsole::Print(QString("[Orientation] Entity '%1'").arg(ent->getName()));
ccConsole::Print("\t- plane fitting RMS: %f",rms); //We always consider the normal with a positive 'Z' by default!
if (N.z < 0.0)
N *= -1.0;
ccConsole::Print("\t- normal: (%f,%f,%f)",N.x,N.y,N.z); //we compute strike & dip by the way
PointCoordinateType dip = , dipDir = ;
ccNormalVectors::ConvertNormalToDipAndDipDir(N,dip,dipDir);
QString dipAndDipDirStr = ccNormalVectors::ConvertDipAndDipDirToString(dip,dipDir);
ccConsole::Print(QString("\t- %1").arg(dipAndDipDirStr)); //hack: output the transformation matrix that would make this normal points towards +Z
ccGLMatrix makeZPosMatrix = ccGLMatrix::FromToRotation(N,CCVector3(,,PC_ONE));
CCVector3 Gt = C;
makeZPosMatrix.applyRotation(Gt);
makeZPosMatrix.setTranslation(C-Gt);
ccConsole::Print("[Orientation] A matrix that would make this plane horizontal (normal towards Z+) is:");
ccConsole::Print(makeZPosMatrix.toString(,' ')); //full precision
ccConsole::Print("[Orientation] You can copy this matrix values (CTRL+C) and paste them in the 'Apply transformation tool' dialog"); plane->setName(dipAndDipDirStr);
plane->applyGLTransformation_recursive(); //not yet in DB
plane->setVisible(true);
plane->setSelectionBehavior(ccHObject::SELECTION_FIT_BBOX); ent->addChild(plane);
plane->setDisplay(ent->getDisplay());
plane->prepareDisplayForRefresh_recursive();
addToDB(plane);
}
else
{
ccConsole::Warning(QString("Failed to fit a plane/facet on entity '%1'").arg(ent->getName()));
}
}
} refreshAll();
updateUI();
}
ccPlane的fit方法:
ccPlane* ccPlane::Fit(CCLib::GenericIndexedCloudPersist *cloud, double* rms/*=0*/)
{
//number of points
unsigned count = cloud->size();
if (count < 3)
{
ccLog::Warning("[ccPlane::Fit] Not enough points in input cloud to fit a plane!");
return 0;
} CCLib::Neighbourhood Yk(cloud); //plane equation
const PointCoordinateType* theLSPlane = Yk.getLSPlane();
if (!theLSPlane)
{
ccLog::Warning("[ccPlane::Fit] Not enough points to fit a plane!");
return 0;
} //get the centroid
const CCVector3* G = Yk.getGravityCenter();
assert(G); //and a local base
CCVector3 N(theLSPlane);
const CCVector3* X = Yk.getLSPlaneX(); //main direction
assert(X);
CCVector3 Y = N * (*X); //compute bounding box in 2D plane
CCVector2 minXY(0,0), maxXY(0,0);
cloud->placeIteratorAtBegining();
for (unsigned k=0; k<count; ++k)
{
//projection into local 2D plane ref.
CCVector3 P = *(cloud->getNextPoint()) - *G; CCVector2 P2D( P.dot(*X), P.dot(Y) ); if (k != 0)
{
if (minXY.x > P2D.x)
minXY.x = P2D.x;
else if (maxXY.x < P2D.x)
maxXY.x = P2D.x;
if (minXY.y > P2D.y)
minXY.y = P2D.y;
else if (maxXY.y < P2D.y)
maxXY.y = P2D.y;
}
else
{
minXY = maxXY = P2D;
}
} //we recenter the plane
PointCoordinateType dX = maxXY.x-minXY.x;
PointCoordinateType dY = maxXY.y-minXY.y;
CCVector3 Gt = *G + *X * (minXY.x + dX / 2) + Y * (minXY.y + dY / 2);
ccGLMatrix glMat(*X,Y,N,Gt); ccPlane* plane = new ccPlane(dX, dY, &glMat); //compute least-square fitting RMS if requested
if (rms)
{
*rms = CCLib::DistanceComputationTools::computeCloud2PlaneDistanceRMS(cloud, theLSPlane);
plane->setMetaData(QString("RMS"),QVariant(*rms));
} return plane;
}
Efficient Ransac shape extract插件调用的模板类Plane实现,可以看到使用的Jacobi特征值分解的方法实现。
template< class PointT >
template< class PointsForwardIt, class WeightsForwardIt >
bool Plane< PointT >::Fit(const PointType &origin, PointsForwardIt begin,PointsForwardIt end, WeightsForwardIt weights)
{
MatrixXX< PointType::Dim, PointType::Dim, ScalarType > c, v;
CovarianceMatrix(origin, begin, end, weights, &c);
VectorXD< PointType::Dim, ScalarType > d;
if(!Jacobi(c, &d, &v))
{
//std::cout << "Jacobi failed:" << std::endl;
//std::cout << "origin = " << origin[0] << "," << origin[1] << "," << origin[2] << std::endl
// << "cov:" << std::endl
// << c[0][0] << c[1][0] << c[2][0] << std::endl
// << c[0][1] << c[1][1] << c[2][1] << std::endl
// << c[0][2] << c[1][2] << c[2][2] << std::endl;
//std::cout << "recomp origin:" << std::endl;
//PointT com;
//Mean(begin, end, weights, &com);
//std::cout << "origin = " << origin[0] << "," << origin[1] << "," << origin[2] << std::endl;
//std::cout << "recomp covariance:" << std::endl;
//CovarianceMatrix(com, begin, end, weights, &c);
//std::cout << "cov:" << std::endl
//<< c[0][0] << c[1][0] << c[2][0] << std::endl
//<< c[0][1] << c[1][1] << c[2][1] << std::endl
//<< c[0][2] << c[1][2] << c[2][2] << std::endl;
//std::cout << "weights and points:" << std::endl;
//WeightsForwardIt w = weights;
//for(PointsForwardIt i = begin; i != end; ++i, ++w)
// std::cout << (*i)[0] << "," << (*i)[1] << "," << (*i)[2]
// << " weight=" << (*w) << std::endl;
return false;
}
for(unsigned int i = 0; i < PointType::Dim; ++i)
d[i] = Math< ScalarType >::Abs(d[i]);
EigSortDesc(&d, &v);
_normal = PointType(v[PointType::Dim - 1]);
_d = -(_normal * origin);
return true;
}
[CC]平面拟合的更多相关文章
- 数据的平面拟合 Plane Fitting
数据的平面拟合 Plane Fitting 看到了一些利用Matlab的平面拟合程序 http://www.ilovematlab.cn/thread-220252-1-1.html
- 蛙蛙推荐: TensorFlow Hello World 之平面拟合
tensorflow 已经发布了 2.0 alpha 版本,所以是时候学一波 tf 了.官方教程有个平面拟合的类似Hello World的例子,但没什么解释,新手理解起来比较困难. 所以本文对这个案例 ...
- 三维点集拟合:平面拟合、RANSAC、ICP算法
ACM算法分类:http://www.kuqin.com/algorithm/20080229/4071.html 一: 拟合一个平面:使用SVD分解,代码里面去找吧 空间平面方程的一般表达式为: A ...
- RANSAC介绍(Matlab版直线拟合+平面拟合)
https://blog.csdn.net/u010128736/article/details/53422070
- PCL利用RANSAC自行拟合分割平面
利用PCL中分割算法. pcl::SACSegmentation<pcl::PointXYZ> seg; ,不利用法线参数,只根据模型参数得到的分割面片,与想象的面片差距很大, pcl:: ...
- 使用matlab进行空间拟合
假设有这么一组数据, x=[4 5 6 7 8 4 8 10]'; y=[56 56 56 56 56 60 60 60]';z=[6 6 6 9 6 19 6 6]'; 要求出其平面方程z=C+Ax ...
- 【Matlab&Mathematica】对三维空间上的点进行椭圆拟合
问题是这样:比如有一个地心惯性系的轨道,然后从轨道上取了几个点,问能不能根据这几个点把轨道还原了? 当然,如果知道轨道这几个点的速度的情况下,根据轨道六根数也是能计算轨道的,不过真近点角是随时间变动的 ...
- RANSAC算法笔记
最近在做平面拟合,待处理的数据中有部分噪点需要去除,很多论文中提到可以使用Ransac方法来去除噪点. 之前在做图像配准时,用到了Ransac算法,但是没有去仔细研究,现在好好研究一番. 参考: ht ...
- 45、Docker 加 tensorflow的机器学习入门初步
[1]最近领导天天在群里发一些机器学习的链接,搞得好像我们真的要搞机器学习似的,吃瓜群众感觉好神奇呀. 第一步 其实也是最后一步,就是网上百度一下,Docker Toolbox,下载下来,下载,安装之 ...
随机推荐
- MIS系统开发利器,实施、维护人员自定义报表的福音,AgileEAS.NET SOA平台动态报表指南
一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...
- sql server存储过程分页,行变列
CREATE PROCEDURE [dbo].[PROC_GetPriviousAndNextDetailContent]@Index varchar(20),--表主键@Table varchar( ...
- 如何在IntelliJ IDEA删除Project
1.直接从菜单 File -> Close Project 关掉此项目,回到欢迎界面 2.在欢迎界面中,鼠标移到你想要删除的项目上(不要点击,一点就打开了),然后按DELETE键即可删除
- redis数据类型之—List
(1)list 简单介绍 list是一个有序的字符串列表,是使用双向列表实现的,可以实现最新消息排行.消息队列等功能. (2) list 常用命令
- UIView--震动效果
//震动效果- (void)shake:(UIView *)view{ CGRect frame = view.frame; CAKeyframeAnimation *shakeAnimation = ...
- iOS 消息推送(APNs) 傻瓜式教程
也可以去我的简书页面查看这篇文章 首先: 1.做iOS消息推送需要真机测试 2.做iOS消息推送需要有付费的开发者账号 是否继续看帖? 先学习一下相关的知识吧! 因为中途可能会遇到一些问题,这篇文章或 ...
- Bulk_Collect_Performance 比较
上一篇讲到了调用集锦,这篇关注一下性能问题吧. DECLARE CURSOR c_tool_list IS SELECT descr d1 FROM hardware; l_descr hardwar ...
- 怎样去除织梦版权信息中的Power by DedeCms
用织梦建站时,网站底部调用的版权信息最后总会多出一个Power by DedeCms链接,此链接是织梦系统中默认的指向织梦官网的外链.本文就介绍两种去除这个外链的方法. 1.为什么要去除Power b ...
- log4j mongoDB配置
log4j.rootCategory=INFO, stdout log4j.appender.stdout=org.springframework.data.document.mongodb.log4 ...
- 使用holder进行内存管理
在C++中,我们使用new 和delete进行自己的内存管理. void test_func() { someType *ptr = new someType; //使用ptr ptr->fun ...