基于QT CC 点云二开及魔改记录

目录

一、功能需要

二、所需处理实现

三、代码借鉴及魔改

四、附加内容分析

五、效果图:

六、部署中遇到的问题


一、功能需要

1.高程图:原理就是按照X/Y/Z按照单个维度的方向上进行着色,一般都是Z向维度

2.边界框:在游戏里面也叫包围盒,可以是不规则的,做碰撞检测用,在工业中就是一个简单的外接包围矩形体

3.LCC:连通域分析切割,空间中点的密集程度分析切块标记分割。

二、所需处理实现

实际的核心功能CC的CoreLib都已经提供,说白了就是API的调用了,唯一要做的就是把相关参数输入和结果输出呈现在我们自己的UI上面。

不过有了前面的魔改项目的相关经验,具体可参考笔者的前面对整个UI界面的重新构造及魔改:

QT cmake cc级联pcl 魔改项目-CSDN博客

所以实际上处理起来也就没什么压力了,但毕竟年纪大了记忆不怎么好,所以主要就是记录一下,谨防遗忘。

三、代码借鉴及魔改

虽然CC都已经有了,但是还是同样的问题需要,从CC代码中分析有用的代码并按照自己的流程放入自己的工程中实现。

所以最重要的还是分析原代码,整个CC的渲染显示部分前面已经做过基本分析,可以参考

QT cmake cc级联pcl 魔改项目-CSDN博客

剩余就是通过界面上的按钮顺腾摸瓜找自己想要的东西了,及过程中遇到的问题,

顺便整理一下过程遇到的代码,如下:

class MainWindow : public QMainWindow,public ccMainAppInterface{

inline  const ccHObject::Container& getSelectedEntities() const override { return m_selectedEntities; }


//LCC 连通域---相关处理逻辑---------比较简单-------
menuSegmentation-》actionLabelConnectedComponents
connect(m_UI->actionLabelConnectedComponents,	&QAction::triggered, this, &MainWindow::doActionLabelConnectedComponents);

doActionLabelConnectedComponents();
{
	.........
	//参数设置界面调用
	........
	//一些调用核心库的常规操作
	.......
	//ccCommon.h 依赖
	.......
	.......
	//连通域的切块 显示相关
	createComponentsClouds()

}

createComponentsClouds(){
    //......
	//对分析出来的点云进行显示等
    //....
}


//高程渲染直接操作内容:
//menuEdit->menuColors->actionSetColorGradient

connect(m_UI->actionSetColorGradient,	&QAction::triggered, this, &MainWindow::doActionSetColorGradient);

doActionSetColorGradient()
{
	/*ccHObject::Container    m_selectedEntities  什么时候被选中更改的
ccEntityAction::setColorGradient(m_selectedEntities, this) 
-->ccEntityAction.h-->
*/
ccEntityAction::setColorGradient(.....)
{
		for (ccHObject* ent : selectedEntities)
		{
			bool lockedVertices = false;
            //此处一直返回0x00,实体内容选中错误,导致类型检测一直不对,返回的错误
			ccGenericPointCloud* cloud = ccHObjectCaster::ToGenericPointCloud(ent,&lockedVertices);
			if (lockedVertices)
			{
				ccUtils::DisplayLockedVerticesWarning(ent->getName(), selectedEntities.size() == 1);
				continue;
			}
			
			if (cloud && cloud->isA(CC_TYPES::POINT_CLOUD)) // TODO
			{
				ccPointCloud* pc = static_cast<ccPointCloud*>(cloud);
				
				bool success = false;
				if (ramp == ccColorGradientDlg::Banding)
					success = pc->setRGBColorByBanding(dim, frequency);
				else
					success = pc->setRGBColorByHeight(dim, colorScale);
				
				if (success)
				{
					ent->showColors(true);
					ent->showSF(false); //just in case
					ent->prepareDisplayForRefresh();
				}
			}
		}
}
		return;

	refreshAll();
	updateUI();
	{
		updateUIWithSelection()
		{//此处更新掉了  m_selectedEntities,

			m_selectedEntities.clear();

			if (m_ccRoot)
			{
				m_ccRoot->getSelectedEntities(m_selectedEntities, CC_TYPES::OBJECT, &selInfo);
			}
		}
		
		//对于本项目没什么用注释掉即可
		//updateMenus();
		//updatePropertiesView();
	}

}

//边界包围盒操作---ROI区域设定----涉及内容比较多,主要是交互式操作选项----
void MainWindow::activateClippingBoxMode(){
/*---------相关头文件依赖项----------
ccReservedIDs.h
ccContourExtractor.h ccContourExtractor.cpp
ccCropTool.h ccCropTool.cpp
ccClippingBoxTool.h ccClippingBoxTool.cpp clippingBoxDlg.ui
ccContourExtractorDlg.h ccContourExtractorDlg.cpp contourExtractorDlg.ui
ccBoundingBoxEditorDlg.h ccBoundingBoxEditorDlg.cpp BoundingBoxEditorDlg.ui
ccClippingBoxRepeatDlg.h ccClippingBoxRepeatDlg.cpp ClippingBoxRepeatDlg.ui

ccClippingBoxTool.cpp{

removeLastContour()->MainWindow::TheInstance();

}
*/

    m_clipTool;//重要参数,CCoreLib核心库工具对象

	m_clipTool->linkWith(win);
	
	if (m_clipTool->start())
	{
		//相关联的界面控件及内容更新,本项目没由那么多,需要更新,直接注释
        //3D视图窗口中上层关联的窗口显示及位置调整
		//registerOverlayDialog(m_clipTool, Qt::TopRightCorner);
		//freezeUI(true);
		//updateOverlayDialogsPlacement();
		//deactivate all other GL windows
		//disableAllBut(win);
		
		//将对应关联操作面板移动到3D视图的窗口位置
        //直接由registerOverlayDialog()方法改造而来
        repositionOverlayDialog(m_clipTool, Qt::TopRightCorner);
		
	}
	else{
		.......
		......
	}
	
}


//3D视图窗口中上层关联的窗口显示及位置调整
void MainWindow::registerOverlayDialog(ccOverlayDialog* dlg, Qt::Corner pos){
	....
	....
	//本项目中不需要MDI 所以核心方法就下面的这个
	repositionOverlayDialog(mdi);
	....
}

	struct ccMDIDialogs
	{
		ccOverlayDialog* dialog;
		Qt::Corner position;

		//! Constructor with dialog and position
		ccMDIDialogs(ccOverlayDialog* dlg, Qt::Corner pos)
			: dialog(dlg)
			, position(pos)
		{}
	};

void repositionOverlayDialog(ccMDIDialogs& mdiDlg){
	
}

void MainWindow::unregisterOverlayDialog(ccOverlayDialog* dialog){

}


}

四、附加内容分析

实际上CC整个界面层qcc项目中所有界面相关管理和点云数据的管理都依赖于:

ccDBRoot,qcc界面层上MDI 树形图控件 点云显示及可视化操作后的结果相关管理类

ccHObject ,CCoreLib对外的一个核心类(承上启下,上与界面层对接,下与核心库交互操作),这个类结构上是同qcc界面层的自定义树形图DBtree需要显示的树形图样式是一致的。

所以这个两个类中的东西相对比较核心的,大概分析如下:

//-------------------ccHObject------------------------------

class QCC_DB_LIB_API ccObject : public ccSerializableObject{}

class QCC_DB_LIB_API ccHObject : public ccObject, public ccDrawableObject{

protected:
	//! Parent
	ccHObject* m_parent;//注意是自身类型对象

	//! Children
	Container m_children;//注意是个容器,点云的实体

public:
    //这个对象都是在对 m_parent,m_children这两个变量的操作,一个父子的关系链

	inline ccHObject* getParent() const { return m_parent; }
	inline unsigned getChildrenNumber() const { return static_cast<unsigned>(m_children.size()); }
	inline ccHObject* getChild(unsigned childPos) const { return (childPos < getChildrenNumber() ? m_children[childPos] : nullptr); }


	int getIndex() const;
	int ccHObject::getIndex() const
	{
		return (m_parent ? m_parent->getChildIndex(this) : -1);
	}	
	
	
	int getChildIndex(const ccHObject* aChild) const;
	int ccHObject::getChildIndex(const ccHObject* child) const
	{
		for (size_t i=0; i<m_children.size(); ++i)
			if (m_children[i] == child)
				return static_cast<int>(i);

		return -1;
	}	
	
	
	
	//ccHObject::Container clouds;
	filterChildren(clouds, true, CC_TYPES::POINT_CLOUD)
	
unsigned ccHObject::filterChildren(	Container& filteredChildren,
									bool recursive/*=false*/,
									CC_CLASS_ENUM filter/*=CC_TYPES::OBJECT*/,
									bool strict/*=false*/,
									ccGenericGLDisplay* inDisplay/*=0*/) const	
{
	for (auto child : m_children)
	{
		if (	(!strict && child->isKindOf(filter))
			||	( strict && child->isA(filter)))
		{
			if (!inDisplay || child->getDisplay() == inDisplay)
			{
				//warning: we have to handle unicity as a sibling may be in the same container as its parent!
				if (std::find(filteredChildren.begin(), filteredChildren.end(), child) == filteredChildren.end()) //not yet in output vector?
				{
					filteredChildren.push_back(child);
				}
			}
		}

		if (recursive)
		{
			child->filterChildren(filteredChildren, true, filter, strict, inDisplay);
		}
	}

	return static_cast<unsigned>(filteredChildren.size());
}	

}
//------关联 DBtree 选中操作---》-ccDBRoot 管理操作类对象-------------
class ccDBRoot : public QAbstractItemModel{

	QStandardItemModel* m_propertiesModel;//选中的实体 属性数据模型
	ccPropertiesTreeDelegate* m_ccPropDelegate;
	
	//包含了整个树控件显示的内容及格式
	ccHObject* m_treeRoot;
	
	//重要--相关的点云对象存储对象的指针 都会存储在此控件item模型的指针中
	QTreeView* m_dbTreeWidget;//本项目中不需要使用直接注释

//构造函数中调用,模型关联到树形图控件上
m_dbTreeWidget->setModel(this);//this ==> ccDBRoot
m_treeRoot = new ccHObject("DB Tree");

//特别重要--------重写自QT 视图模型 接口------对点云实体数据做的一些显示操作
bool ccDBRoot::setData(const QModelIndex &index, const QVariant &value, int role)
{
	if (index.isValid())
	{
		if (role == Qt::EditRole)
		{
			if (value.toString().isEmpty())
			{
				return false;
			}

			ccHObject *item = static_cast<ccHObject*>(index.internalPointer());
			assert(item);
			if (item)
			{
				item->setName(value.toString());

				//particular cases:
				// - labels name is their title (so we update them)
				// - name might be displayed in 3D
				if (item->nameShownIn3D() || item->isKindOf(CC_TYPES::LABEL_2D))
					if (item->isEnabled() && item->isVisible() && item->getDisplay())
						item->getDisplay()->redraw();

				reflectObjectPropChange(item);

				emit dataChanged(index, index);
			}

			return true;
		}
		else if (role == Qt::CheckStateRole)
		{
			ccHObject *item = static_cast<ccHObject*>(index.internalPointer());
			assert(item);
			if (item)
			{
				if (value == Qt::Checked)
					item->setEnabled(true);
				else
					item->setEnabled(false);

				redrawCCObjectAndChildren(item);
				//reflectObjectPropChange(item);
			}

			return true;
		}
	}
	return false;
}

//特别重要--------重写自QT 视图模型 接口----对应obj 与 item 关联的 对应索引 点云实体索引
data(...);
parent(....);
index(ccHObject* obj){
	assert(object);

	if (object == m_treeRoot)
	{
		return QModelIndex();
	}

	ccHObject* parent = object->getParent();
	if (!parent)
	{
		//DGM: actually, it can happen (for instance if the entity is displayed in the local DB of a 3D view)
		//ccLog::Error(QString("An error occurred while creating DB tree index: object '%1' has no parent").arg(object->getName()));
		return QModelIndex();
	}

	int pos = parent->getChildIndex(object);
	assert(pos >= 0);

	return createIndex(pos, 0, object);//---重要 关联到 对应索引的 Item  QModelIndex
}

//添加点云数据时候使用,操作比较绕...对链的操作,
void ccDBRoot::addElement(ccHObject* object, bool autoExpand/*=true*/)
{
	if (!m_treeRoot)
	{
		assert(false);
		return;
	}
	if (!object)
	{
		assert(false);
		return;
	}
	bool wasEmpty = (m_treeRoot->getChildrenNumber() == 0);

	ccHObject* parentObject = object->getParent();
    //parentObject == 0 ==》object->getParent()==0 条件才会满足执行此逻辑
	if (!parentObject)
	{
		parentObject = m_treeRoot;//object->getParent() = m_treeRoot。
		m_treeRoot->addChild(object);//
	}
	else
	{
		//DGM TODO: how could we check that the object is not already inserted in the DB tree?
		//The double insertion can cause serious damage to it (not sure why excatly though).

		//The code below doesn't work because the 'index' method will always return a valid index
		//as soon as the object has a parent (index creation is a purely 'logical' approach)
		//QModelIndex nodeIndex = index(object);
		//if (nodeIndex.isValid())
		//	return;
	}

	//look for insert node index in tree
	QModelIndex insertNodeIndex = index(parentObject);//行数第1级
	int childPos = parentObject->getChildIndex(object);//ccHObject_.中的child 是一个 vector<ccHObject>的动态数组容器 并列

//QModelIndex &parent,int first,int last,按照实参只可能是两级,一个顶级和一个子级
//(子级下面并列的)
	//row insertion operation (start)
	beginInsertRows(insertNodeIndex, childPos, childPos);

	//row insertion operation (end)
	endInsertRows();

	if (autoExpand)//树型图控件是否要展开节点操作,本项目中不需要 全部注释掉
	{
		//expand the parent (just in case)
		m_dbTreeWidget->expand(index(parentObject));
		//and the child
		m_dbTreeWidget->expand(index(object));
	}
	else //if (parentObject)
	{
		m_dbTreeWidget->expand(insertNodeIndex);
	}

	if (wasEmpty && m_treeRoot->getChildrenNumber() != 0)
	{
		emit dbIsNotEmptyAnymore();//发射信号函数回到Mainwindows中去做一些ui刷新
	}
}



//重要获取选中的 点云实体对象
getSelectedEntities(...)
{
	selectedEntities.clear();

	QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
	QModelIndexList selectedIndexes = qism->selectedIndexes();

	try
	{
		int selCount = selectedIndexes.size();
		for (int i = 0; i < selCount; ++i)
		{
			//重要----selectedEntities----
			//来源于m_dbTreeWidget->selectionModel()->selectedIndexes();
			//关联到ccDBRoot->m_treeRoot对象
			ccHObject* object = static_cast<ccHObject*>(selectedIndexes[i].internalPointer());
			if (object && object->isKindOf(filter))
				selectedEntities.push_back(object);
		}
	}
	catch (const std::bad_alloc&)
	{
		//not enough memory!
	}
	
	。。。。。

}


connect(m_dbTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ccDBRoot::changeSelection);

void ccDBRoot::changeSelection(const QItemSelection & selected, const QItemSelection & deselected){



			ccHObject* element = static_cast<ccHObject*>(selectedItems.at(i).internalPointer());
			assert(element);
			if (element)
			{
				element->setSelected(true);
				element->prepareDisplayForRefresh();
			}


	updatePropertiesView();//重要---对模型填充,包括选中 边界盒等
	MainWindow::RefreshAllGLWindow();
	emit selectionChanged();//发射回 MainWindow 中的函数将选中的点云实体更新
}




//右键菜单
connect(m_dbTreeWidget,&QWidget::customContextMenuRequested,	this, &ccDBRoot::showContextMenu);
==>{
	connect(m_toggleSelectedEntitiesColor,		&QAction::triggered,					this, &ccDBRoot::toggleSelectedEntitiesColor);
	toggleSelectedEntitiesProperty();
	{
		ccHObject* item = static_cast<ccHObject*>(selectedIndexes[i].internalPointer());
		if (!item)
		{
			assert(false);
			continue;
		}
		switch (prop)
		{
		case TG_ENABLE: //enable state
			item->setEnabled(!item->isEnabled());
			break;
		case TG_VISIBLE: //visibility
			item->toggleVisibility();
			break;
		case TG_COLOR: //color
			item->toggleColors();
			break;
		case TG_NORMAL: //normal
			item->toggleNormals();
			break;
		case TG_SF: //SF
			item->toggleSF();
			break;
		case TG_MATERIAL: //Materials/textures
			item->toggleMaterials();
			break;
		case TG_3D_NAME: //3D name
			item->toggleShowName();
			break;
		}
		item->prepareDisplayForRefresh();
	
	}
	
	
}

}

-------以下---2023---10----07----补充----

实际结构分布:

m_treeRoot{ (ccHObject*)
	parent = 0x00
	child
		[0](ccHObject*)
			{
				parent = m_treeRoot
				child[0](ccPointCould* /ccHObject*)
			}
		[1](ccHObject*)
			{
				parent = m_treeRoot
				child[0](ccPointCould* /ccHObject*)
			}
			
}

-------以上---2023---10----07----补充----

五、效果图:

高程图:

BoundBox

LCC连通域:

六、部署中遇到的问题

实际整个项目已经初具雏形并且尝试了实际部署,过程中遇到的问题及记录如下:

缺少 QT 平台相关dll,参考下方链接:

https://blog.csdn.net/Mmagic1/article/details/109603056

通过安装的QT 快速 将依赖的qt dll 拷贝到目录下。
注意:qwindows 这个dll需要在 platforms/目录下

plugins/QPCL_IO_PLUGIN.dll
plugins/QPCL_PLUGIN.dll
对应目录下有这两个dll 却是加载失败,
这两个dll 相关依赖项没找到,安装pcl all in one 1.10.1 即可

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值