嘮叨
本文分三篇來介紹一個(gè)完整的CloudComapre插件的編寫教程寺枉,分別是插件框架篇、數(shù)據(jù)結(jié)構(gòu)篇胳搞、算法實(shí)現(xiàn)篇卸例。
這是第三篇,算法實(shí)現(xiàn)篇肌毅,你可以根據(jù)本文改成自己的插件筷转,待卿臨幸。
qSAF源碼:Github . qSAF
前文概要
在上回中悬而,我們知道了點(diǎn)云中掃描角度的存儲(chǔ)結(jié)構(gòu)呜舒,下面我們來講qSAF的具體實(shí)現(xiàn)。
UI界面
新建QT設(shè)計(jì)器界面類笨奠,命名為ccSAFDlg
袭蝗,在ccSAFDlg.ui
文件設(shè)計(jì)簡單的界面。
因?yàn)槲覀冎恍枰粋€(gè)范圍般婆,一個(gè)確認(rèn)取消鍵到腥,所以我把它弄成這樣子:
doubleSpinBox
要設(shè)置范圍:0.00
到90.00
,默認(rèn)值分別設(shè)為20.00
和70.00
腺兴。
ccSAFDlg.h
:
#ifndef CCSAFDLG_H
#define CCSAFDLG_H
#include "ui_SAFDlg.h"
#include <QDialog>
namespace Ui {
class ccSAFDlg;
}
class ccSAFDlg : public QDialog, public Ui::ccSAFDlg
{
Q_OBJECT
public:
explicit ccSAFDlg(QWidget *parent = 0);
protected slots:
//! Saves (temporarily) the dialog paramters on acceptation
void saveSettings();
};
#endif // CCSAFDLG_H
ccSAFDlg.cpp
:
#include "ccSAFDlg.h"
//定義兩個(gè)靜態(tài)閾值左电,并初始化
static double threshold_1 = 20;
static double threshold_2 = 70;
ccSAFDlg::ccSAFDlg(QWidget *parent) : QDialog(parent), Ui::ccSAFDlg()
{
setupUi(this);
//關(guān)聯(lián)信號(hào)槽
connect(buttonBox, SIGNAL(accepted()), this, SLOT(saveSettings()));
//初始化設(shè)置閾值
doubleSpinBox_1->setValue(threshold_1);
doubleSpinBox_2->setValue(threshold_2);
}
void ccSAFDlg::saveSettings()
{
//OK后重新賦值
threshold_1 = doubleSpinBox_1->value();
threshold_2 = doubleSpinBox_2->value();
}
現(xiàn)在界面就做好了。
插件doAction實(shí)現(xiàn)
至于doAction的實(shí)現(xiàn)页响,點(diǎn)云其中的數(shù)據(jù)結(jié)構(gòu)篓足,可以參考第二篇,數(shù)據(jù)結(jié)構(gòu)篇
簡單地說闰蚕,我們需要:
- 用
Scan Angle Rank
栈拖,通過getScalarFieldIndexByName()
獲得掃描角度在標(biāo)量域中的索引 - 用索引,通過
getScalarField()
獲得掃描角度標(biāo)量域指針 - 用指針没陡,通過
getValue()
獲得每個(gè)點(diǎn)的值 - 比較掃描角度值與用戶輸入?yún)^(qū)間的大小涩哟,把合適的值存儲(chǔ)起來
- 把合適值封裝成點(diǎn)云實(shí)體
- 顯示在界面上
大體的算法思路上是沒有問題的,但是有個(gè)糾結(jié)的地方盼玄,就是是否使用進(jìn)度條贴彼。
實(shí)測SAF處理一個(gè)雷達(dá)文件,
- 使用進(jìn)度條耗時(shí):129.1s
- 不用進(jìn)度條耗時(shí):3.5s
這種壓倒性的差距讓我果斷砍掉真·進(jìn)度條埃儿,沒錯(cuò)器仗!我使用假·進(jìn)度條,就是不會(huì)動(dòng)的進(jìn)度條。
這樣短時(shí)間的處理使用假·進(jìn)度條精钮,既不會(huì)降低處理速度威鹿,也不會(huì)降低用戶體驗(yàn)~
下面就是完整代碼,注釋中有真·進(jìn)度條的實(shí)現(xiàn)([進(jìn)度條]
)轨香,但不推薦使用
void qSAF::doAction()
{
//當(dāng)插件加載時(shí)忽你,m_app應(yīng)該已經(jīng)被CC初始化了
assert(m_app);
if (!m_app)
return;
//獲取選擇的實(shí)體
const ccHObject::Container& selectedEntities = m_app->getSelectedEntities();
//獲取選擇的實(shí)體數(shù)量
size_t selNum = selectedEntities.size();
//確保只選擇一個(gè)實(shí)體
if (selNum != 1)
{
m_app->dispToConsole("[SAF] Select only one cloud!", ccMainAppInterface::ERR_CONSOLE_MESSAGE);
return;
}
ccHObject* ent = selectedEntities[0];
assert(ent);
//確保選擇的實(shí)體是POINT_CLOUD類型
if (!ent || !ent->isA(CC_TYPES::POINT_CLOUD))
{
m_app->dispToConsole("[SAF] Select a real point cloud!", ccMainAppInterface::ERR_CONSOLE_MESSAGE);
return;
}
//從選擇的實(shí)體中轉(zhuǎn)換成ccPointCloud*類型
ccPointCloud* pc = static_cast<ccPointCloud*>(ent);
//獲取點(diǎn)云的數(shù)量m_count
unsigned count = pc->size();
//初始化閾值變量
static double threshold_1 = 20;
static double threshold_2 = 70;
double threshold_temp = 0;
//顯示插件ui窗體
{
ccSAFDlg safDlg(m_app->getMainWindow());
safDlg.doubleSpinBox_1->setValue(threshold_1);
safDlg.doubleSpinBox_2->setValue(threshold_2);
if(!safDlg.exec())
{
return;
}
//存儲(chǔ)閾值
threshold_1 = safDlg.doubleSpinBox_1->value();
threshold_2 = safDlg.doubleSpinBox_2->value();
}
//顯示進(jìn)度條窗體
QProgressDialog pDlg;
pDlg.setWindowTitle("SAF");
pDlg.setLabelText(QString("Scan Angle Filter\nfrom %1 to %2").arg(threshold_1).arg(threshold_2));
//[進(jìn)度條]設(shè)置進(jìn)度條總范圍
//pDlg.setRange(0, count);
pDlg.setCancelButton(0);
pDlg.show();
QApplication::processEvents();
QElapsedTimer timer;
//計(jì)時(shí)開始
timer.start();
ScalarType scanAngle;
CCLib::ReferenceCloud rangeAnglerc(pc);
//確保 threshold_1 小于 threshold_2
if(threshold_1 > threshold_2)
{
threshold_temp = threshold_1;
threshold_1 = threshold_2;
threshold_2 = threshold_temp;
}
//[進(jìn)度條]進(jìn)度條的取消SAF按鈕
//bool wasCancelled = false;
//獲取 Scan Angle Rank 的索引
int scanAngleSFIndex = pc->getScalarFieldIndexByName("Scan Angle Rank");
//[重點(diǎn)]遍歷每個(gè)點(diǎn)的操作
for(unsigned i = 0; i < count; ++i)
{
//獲取每個(gè)點(diǎn)的掃描角度
scanAngle = pc->getScalarField(scanAngleSFIndex)->getValue(i);
//取掃描角度的絕對值
if(scanAngle < 0)
{
scanAngle = -scanAngle;
}
//如果掃描角度在給定的閾值范圍,則添加它的索引到參考云
if(threshold_1 <= scanAngle && scanAngle <= threshold_2)
{
rangeAnglerc.addPointIndex(i);
}
// //[進(jìn)度條]重置進(jìn)度條
// pDlg.setValue(i);
// QCoreApplication::processEvents();
// //[進(jìn)度條]取消SAF處理
// if (pDlg.wasCanceled())
// {
// wasCancelled = true;
// break;
// }
}
//把 ReferenceCloud 類型克隆成 ccPointCloud 類型
ccPointCloud* rangeAnglepc = pc->partialClone(&rangeAnglerc);
//判斷rangeAnglepc是否為空臂容,即所選范圍內(nèi)是否有點(diǎn)
if(!rangeAnglepc)
{
m_app->dispToConsole("[SAF] Failed to extract the range angle subset.", ccMainAppInterface::ERR_CONSOLE_MESSAGE);
return;
}
//計(jì)算SAF后點(diǎn)數(shù)所占的百分比和SAF過程所花的時(shí)間
m_app->dispToConsole(QString("[SAF] %1% of scan angle points are filtered").arg((rangeAnglerc.size() * 100.0) / count, 0, 'f', 2), ccMainAppInterface::STD_CONSOLE_MESSAGE);
m_app->dispToConsole(QString("[SAF] Timing: %1 s.").arg(timer.elapsed() / 1000.0, 0, 'f', 1), ccMainAppInterface::STD_CONSOLE_MESSAGE);
//關(guān)閉進(jìn)度條
pDlg.close();
QApplication::processEvents();
// //[進(jìn)度條]取消SAF
// if (wasCancelled)
// {
// m_app->dispToConsole("[SAF] SAF was cancelled", ccMainAppInterface::STD_CONSOLE_MESSAGE);
// return;
// }
//隱藏原始點(diǎn)云
pc->setEnabled(false);
//添加新的一組DB實(shí)體
ccHObject* cloudContainer = new ccHObject(pc->getName() + QString("_saf"));
//設(shè)置新點(diǎn)云并添加到實(shí)體
rangeAnglepc->setVisible(true);
rangeAnglepc->setName("SAF Point Cloud");
cloudContainer->addChild(rangeAnglepc);
//添加實(shí)體到DB樹
m_app->addToDB(cloudContainer);
//刷新
m_app->refreshAll();
}
效果
結(jié)語
經(jīng)過了三篇的學(xué)習(xí)科雳,終于實(shí)現(xiàn)了個(gè)完整的插件。
回顧我們學(xué)習(xí)的路線:插件框架 -> 數(shù)據(jù)結(jié)構(gòu) -> 算法實(shí)現(xiàn)
我們不僅從中學(xué)會(huì)了CC插件的編寫脓杉,也學(xué)到了QT的pro文件編寫炸渡、QT界面設(shè)計(jì)、CC運(yùn)作流程丽已、點(diǎn)云數(shù)據(jù)結(jié)構(gòu)等。
而我在學(xué)習(xí)這個(gè)插件編寫的過程收獲更多买决,因?yàn)槲沂强创a兩個(gè)月沛婴,寫代碼兩小時(shí),Debug兩天(差不多啦不要糾結(jié)為什么222)
看代碼的過程是非常痛苦的督赤,CC里面大量的模板編程思想嘁灯,接口設(shè)計(jì)思想,還有去他繼承誰爸爸的爸爸……
但是期間確實(shí)學(xué)到很多躲舌,以此作為分享丑婿,望共勉!
我的博客:https://blog.huihut.com/
轉(zhuǎn)載請注明出處:http://blog.huihut.com/2017/04/27/CloudCompareSAFPlugin_3_Algorithm/