我之前写过一篇博客Qt 5.9使用VTK显示点云,可惜其中很多部分都是失败的,而且排版糟糕,不过其中PCL的安装以及在VS中使用PCL的方法是值得参考的,起码我通过这种方式安装的PCL在VS上一直用得OK。但是用了一段时间还是有一些小问题,不致命但是极其恶心,比如
- 在VS中使用PCL我怀疑加入的附加依赖项过多,经常导致
IntelliSense
卡半天,自动补全功能近乎废了,对于我这种“面向自动补全开发”的程序猿来说无异于灭顶之灾 - VS中使用Qt做界面还是很麻烦,虽然可以装一个Qt的插件使用Qt Designer设计界面,但总还是不如Qt自己的Creator好
- 另外在VS中使用Qt库还是不太直接
鉴于此,本着“生命不息,折腾不止”的原则,把PCL折腾到Qt中来。
1.环境
我的环境和之前博客中的不太一样,但是安装与配置方法大同小异。
- 使用的win10系统。小问题,没差别
- 使用的VS2015不再是2013(2015对应的是VC14编译器,与2013不一样),小问题
- 使用的是pcl-1.8.1-msvc2015-x86版本,也就是32位的,安装很简单——在这里下载,提取码:
utw2
;直接点击PCL-1.8.1-AllInOne-msvc2015-win32.exe
安装,完成后需要像博客中第一部分PCL的安装中说明的一样配置环境变量,之后配置属性表(这里我给出的安装包里有配好的属性表,如果你的安装目录不一样,只需要更改一下包含目录与库目录即可);完成以上就可以在VS中使用PCL了 - 我的Qt版本是Qt5.8.0,没有装
MINGW
版本的,MSVC64位与32位的编译套件都安装了(如果要在Qt中使用PCL,那么Qt必须与PCL的版本相协调,因为我的PCL装的32位的,无可奈何只能再装一个MSVC32位版的Qt编译套件;Qt的安装过程略,很简单,下载,安装即可)
2.编译VTK
实际上在Qt的项目中配置一下包含目录、外部库位置即可(按照第3部分),但是最后配置好后会发现PCL能正常使用但是涉及到其中第三方库VTK
的部分功能不能使用,故这里重新编译下VTK
库。
VTK
源码下载
前两步可直接跳过使用我编译好的,除非你的版本不一样,得自己编译。这里用PCL1.8.1配套的VTK8.0,源码下载地址,提取码:2f5x
。- CMAKE配置VTK for Qt编译环境与源码编译
2.1. 打开CMAKE配置项目(CMAKE随便装一个版本),设置源代码目录与构建目录,如下:
2.2. 点击configure
,选择VS2015generator选择win32平台(与PCL同平台)
2.3. 继续configure
2.4. 之后是配置项目构建的一些路径以及项目的配置,选中BUILD_SHARED_LIBS和VTK_GROUP_QT(对Qt的支持),接下来继续configure
会提示配置QMAKE路径与Qt版本,配置完了后继续configure
,然后是配置QT的安装目录以及其一些子目录,按照本机路径配置即可(注意也要指定同平台的Qt,比如VS2015WIN32,我这里使用的x64的Qt路径,倒了大霉重新编译)
这里有一点一定要注意,要设置VTK的渲染后端为OpenGL而不是OpenGL2,如图
2.5. 一直configure
直到标红消失,然后generate
,在构建目录下会出现生成的项目文件VTK.sln,使用VS(以管理员身份运行)打开这个项目,选择ALL_BUILD-右键-生成
2.6. 生成完了选择INSTALL-右键-仅用于项目-仅生成INSTALL,会在C:\\Program Files(x86)\\
下生成一个VTK文件夹,这即是最终要拷到PCL中使用的东西。
2.7. 将以上生成的VTK文件夹下的bin目录下所有dll
文件名称改成-gd.dll
格式,lib文件夹下lib
文件名称改为-gd.lib
格式,然后再VS中使用release模式在执行一遍以上生成与安装步骤,将其dll
与lib
拷到之前的VTK文件夹下,大功告成。
将VTK下dll
与lib
批量改名可使用以下python脚本
import os
def rename_file(old):
#将lib文件或dll文件改名成*-gd.lib或*-gd.dll
if old[::-1].find('bil.')==0 or old[::-1].find('lld.')==0:
path=os.path.dirname(old)
f_name=os.path.basename(old)
suf=f_name[-4:]
f_name=f_name[:-4]
return path+'\\'+f_name+'-gd'+suf
else:
return old
root='D:\\VTK'
paths=[root+'\\bin',root+'\\lib']
for p in paths:
os.chdir(p)
fs=os.listdir()
fs_old=[p+'\\'+f for f in fs]
fs_new=[rename_file(p+'\\'+f) for f in fs]
for (f_old,f_new) in zip(fs_old,fs_new):
os.rename(f_old,f_new)
- 编译后库的放置
编译后文件下载,提取码:2f5x
;将以上编译好的VTK文件夹复制到PCL安装目录下的3rdParty\\
下覆盖同名文件夹(记得先备份)
3.Qt项目配置
项目配置与VS中类似,无非是配置包含目录、添加外部库,不过VS中添加外部库分为了添加库目录与附加依赖项两部分。在Qt中添加包含目录的方式是在.pro
文件中使用语句INCLUDEPATH+=包含目录
;添加外部库的方式可以选中项目-右键-添加库-外部库-浏览到库文件位置(即lib文件位置),在Qt中使用PCL需要添加的库有lib
目录下的库,还有3rdParty
目录下各个第三方库目录下的各自的lib
文件夹中的lib
文件,这些文件洋洋洒洒有几百条,一个一个加入配置文件中将十分费事,可以通过以下python脚本批量一次性生成配置文件:
import re
import os
def isDebug(lib):
return len(re.findall(r'.+debug\..+',lib))!=0 or len(re.findall(r'.+-gd-[1-9_]+\..+',lib))!=0 or len(re.findall(r'.+-gd\..+',lib))!=0 or len(re.findall(r'.+_d\..+',lib))!=0
def get_libs():
return [os.path.abspath('.')+'\\'+f for f in os.listdir() if len(re.findall(r'.+\.lib',f))!=0]
def get_debug_release(libs):
dbg=[d for d in libs if isDebug(d)]
rls=[r for r in libs if not isDebug(r)]
return dbg,rls
def get_pairs(dbg,rls):
d_mod=list(dbg)
r_mod=list(rls)
d_mod=[d.replace('_debug','') for d in d_mod]
r_mod=[r.replace('_release','') for r in r_mod]
d_mod=[d.replace('-gd-','-') for d in d_mod]
d_mod=[d.replace('-gd.','.') for d in d_mod]
d_mod=[d.replace('_d.','.') for d in d_mod]
d_match=[dbg[i] for i in range(len(dbg)) if d_mod[i] in r_mod]
r_match=[rls[r_mod.index(d_mod[i])] for i in range(len(d_match))]
return d_match,r_match
root=os.path.abspath('.')
all_dbg=[]
all_rls=[]
paths=[root+'/lib/',root+'/3rdParty/Boost/lib/',root+'/3rdParty/FLANN/lib/',root+'/3rdParty/Qhull/lib/',root+'/3rdParty/VTK/lib/']
for p in paths:
os.chdir(p)
libs=get_libs()
debug,release=get_debug_release(libs)
all_dbg+=debug
all_rls+=release
all_dbg+=['D:\\PCL-1.8.1\\3rdParty\\OpenNI2\\Lib\\OpenNI2.lib']
all_rls+=['D:\\PCL-1.8.1\\3rdParty\\OpenNI2\\Lib\\OpenNI2.lib']
all_dbg=[item.replace('\\','/') for item in all_dbg]
all_rls=[item.replace('\\','/') for item in all_rls]
lib_d,lib_r=get_pairs(all_dbg,all_rls)
text=''
#添加包含目录
text+=('INCLUDEPATH += '+root.replace('\\','/')+'/include/pcl-1.8\n')
text+=('INCLUDEPATH += '+root.replace('\\','/')+'/3rdParty/Boost/include/boost-1_64\n')
text+=('INCLUDEPATH += '+root.replace('\\','/')+'/3rdParty/Eigen/eigen3\n')
text+=('INCLUDEPATH += '+root.replace('\\','/')+'/3rdParty/FLANN/include\n')
text+=('INCLUDEPATH += '+root.replace('\\','/')+'/3rdParty/OpenNI2/Include\n')
text+=('INCLUDEPATH += '+root.replace('\\','/')+'/3rdParty/Qhull/include\n')
text+=('INCLUDEPATH += '+root.replace('\\','/')+'/3rdParty/VTK/include/vtk-8.0\n\n')
#添加依赖项
for (d,r) in zip(lib_d,lib_r):
path=os.path.dirname(d)
d_name=os.path.basename(d).replace('.lib','')
r_name=os.path.basename(r).replace('.lib','')
text+=('win32:CONFIG(release, debug|release): LIBS += -L'+path+'/'+' -l'+r_name+'\n')
text+=('else:win32:CONFIG(debug, debug|release): LIBS += -L'+path+'/'+' -l'+d_name+'\n')
text+=('else:unix: LIBS += -L'+path+'/'+' -l'+r_name+'\n')
text+='\n'
#导出
with open(root+'/qt_config.txt','w') as f:
f.write(text)
将以上python脚本放在PCL根目录下运行,将生成的qt_config.txt
文件中的所有内容拷贝到.pro
文件中即可。为避免每一次都需要配置,新建一个PCL.pri
文件,将配置内容添加到其中,然后在.pro
文件中include(PCL.pri)
即可完成配置(类似VS中使用配置好的属性表一样)
4.测试
- 本来想用QVTKWidget显示点云,经过测试还是失败,如果避免使用可视化方面的功能上述配置已经没有错误了,而且也可以使用简单的
CloudViewer
来显示点云(虽然性能渣得一匹,一个几万个点的点云只要随便转一下马上就能卡死,比起CloudCompare的加载上百万点甚至千万点可谓差的不是一星半点)如下为一个简单的测试结果:
- 将VTK文件夹下
plugin
目录下的QVTKWidget.dll
放到QT Creator的插件目录下确实能用,在Creator中也能像一般插件一样拖动使用,但是debug的时候程序意外退出,只能以release模式运行。debug时异常提示:QWidget: Must construct a QApplication before a QWidget 程序异常结束。
这里显示用的是VTK库,VTK库底层用的OpenGL渲染,库又调用库,层次繁杂。放弃 - Qt中使用PCL一样卡死自动补全,归根到底还是这个库太臃肿了,递归检索文件太艰难