关于我想在windows上编译colmap这件事
前几天尝试在windows上编译colmap,途中遇到了很多的问题,在这里记录以下,方便以后参考,以下内容仅供参考,如有错误欢迎指正,也欢迎大家有问题在评论区讨论。
环境
windows10 专业版20H2
cuda 10.0
VS2015
QT 5.9.3
cmake 3.18.5
另外可能需要git、7z等软件
编译流程
大体的编译流程如下,先手动编译Boost(也可以现在编译好的),之后编译CGAL,最后编译colmap,其中编译colmap才是遇到坑最多的地方
编译Boost
1.去官网下载一个Boost版本,最好不要太新,不然可能出问题(直觉),这里我用的是1.66.0版本
2. 解压后执行booststrap.bat脚本,之后会生成b2.exe和bjam.exe两个可执行文件,在编译时只用到了b2.exe
3. 在这篇文章的帮助下可以对Boost的编译有更进一步的了解,这里本人在执行时使用的参数如下
b2.exe install --toolset=msvc-14.0 --prefix="E:\3DAbout\ThirdParty\boost_1_66_0\msvc-140_64" link=static runtime-link=shared threading=multi architecture=x86 address-model=64 debug release
因为本人使用的时VS2015因此对应的toolset是msvc-14.0,其它版本的对应关系可以参考这篇文章,这里prefix指定存放编译后文件的目录
4.经过漫长的等待就可以看到在prefix指定的目录下生成了include和lib库
至此Boost编译完成,为了后面表述方便用<boost_path> 指代prefix所指定的目录
编译CGAL
1.去官网下载一个CGAL安装文件,这里实际上下载的是源码,安装完成后还是需要自己使用cmake进行编译,编译完成后将编译好的文件复制一份保留之后将cgal卸载即可,这里下载4.14.3版本
2.之后使用cmake进行编译,configure时最好用64为哦,后面只需要指定Boost_DIR和Boost_INCLUDE_DIR,前者是<boost_path>,后者是<boost_path>\include\boost-1_66,之后configure,generate
3.在build文件夹下找到CGAL.sln,打开编译生成对应的版本即可(Release版本必须有哦)
编译colmap
1.从github上下载colmap,这里下载的是3.5版本(同样不是最新版)
2.这里咱们使用scripts目录下的build.py脚本进行编译安装,但是你直接使用的话会存在网络连接错误等问题,emmm,具体原因有一个是eigen那个库好像删了?然后其它的话也是下载的巨慢,因此采用的方法是咱们自己把库下载下来,扔进去就好了,可以去刚才的那个网盘下载。
3.在colmap根目录下创建一个build文件夹(这里可以自定义,后面在编译时能找到就好),将刚才网盘里的“colmap编译需要的第三方库.zip”中所有的库在build目录下解压,解压时最好选“在当前目录解压”哦,之后将所有的文件夹重命名如下
图中的colmap目录可以先不管
4.之后修改scripts\build.py文件,大体的意思就是将所有需要下载以及重命名的地方注释掉,直接用咱们自己下载的库,我在网盘里提供的库与colmap3.5所需要的库相对应,如果你装其它版本的colmap就下载它所指定的版本,去官网或者github上下载就好了,没啥难的,说明下build_cmake_project函数会在第三方库下面创建__build__目录用于保存编译后的结果,需要调用cmake,因此你需要确保path里加了cmake。下面提供我对整个脚本的更改(只列出了更改过的函数)
def build_eigen(args):
path = os.path.join(args.build_path, "eigen")
# if os.path.exists(path):
# return
# url = "https://bitbucket.org/eigen/eigen/get/3.3.4.zip"
# archive_path = os.path.join(args.download_path, "eigen-3.3.4.zip")
# download_zipfile(url, archive_path, args.build_path,
# "e337acc279874bc6a56da4d973a723fb")
# shutil.move(glob.glob(os.path.join(args.build_path, "eigen-*"))[0], path)
build_cmake_project(args, os.path.join(path, "__build__"))
def build_freeimage(args):
path = os.path.join(args.build_path, "freeimage")
# if os.path.exists(path):
# return
if PLATFORM_IS_WINDOWS:
# url = "https://kent.dl.sourceforge.net/project/freeimage/" \
# "Binary%20Distribution/3.17.0/FreeImage3170Win32Win64.zip"
# archive_path = os.path.join(args.download_path, "freeimage-3.17.0.zip")
# download_zipfile(url, archive_path, args.build_path,
# "a7e6f2f261e72260ec5b91c2a0f4bde3")
# shutil.move(os.path.join(args.build_path, "FreeImage"), path)
copy_file_if_not_exists(
os.path.join(path, "Dist/x64/FreeImage.h"),
os.path.join(args.install_path, "include/FreeImage.h"))
copy_file_if_not_exists(
os.path.join(path, "Dist/x64/FreeImage.lib"),
os.path.join(args.install_path, "lib/FreeImage.lib"))
copy_file_if_not_exists(
os.path.join(path, "Dist/x64/FreeImage.dll"),
os.path.join(args.install_path, "lib/FreeImage.dll"))
else:
# url = "https://kent.dl.sourceforge.net/project/freeimage/" \
# "Source%20Distribution/3.17.0/FreeImage3170.zip"
# archive_path = os.path.join(args.download_path, "freeimage-3.17.0.zip")
# download_zipfile(url, archive_path, args.build_path,
# "459e15f0ec75d6efa3c7bd63277ead86")
# shutil.move(os.path.join(args.build_path, "FreeImage"), path)
if PLATFORM_IS_MAC:
with fileinput.FileInput(os.path.join(path, "Makefile.gnu"),
inplace=True, backup=".bak") as fid:
for line in fid:
if "cp *.so Dist/" in line:
continue
if "FreeImage: $(STATICLIB) $(SHAREDLIB)" in line:
line = "FreeImage: $(STATICLIB)"
print(line, end="")
elif PLATFORM_IS_LINUX:
with fileinput.FileInput(
os.path.join(path, "Source/LibWebP/src/dsp/"
"dsp.upsampling_mips_dsp_r2.c"),
inplace=True, backup=".bak") as fid:
for i, line in enumerate(fid):
if i >= 36 and i <= 44:
line = line.replace("%[\"", "%[\" ")
line = line.replace("\"],", " \"],")
print(line, end="")
with fileinput.FileInput(
os.path.join(path, "Source/LibWebP/src/dsp/"
"dsp.yuv_mips_dsp_r2.c"),
inplace=True, backup=".bak") as fid:
for i, line in enumerate(fid):
if i >= 56 and i <= 58:
line = line.replace("\"#", "\"# ")
line = line.replace("\"(%", " \"(%")
print(line, end="")
subprocess.call(["make", "-f", "Makefile.gnu",
"-j{}".format(multiprocessing.cpu_count())], cwd=path)
copy_file_if_not_exists(
os.path.join(path, "Source/FreeImage.h"),
os.path.join(args.install_path, "include/FreeImage.h"))
copy_file_if_not_exists(
os.path.join(path, "libfreeimage.a"),
os.path.join(args.install_path, "lib/libfreeimage.a"))
def build_glew(args):
path = os.path.join(args.build_path, "glew")
# if os.path.exists(path):
# return
# url = "https://kent.dl.sourceforge.net/project/glew/" \
# "glew/2.1.0/glew-2.1.0.zip"
# archive_path = os.path.join(args.download_path, "glew-2.1.0.zip")
# download_zipfile(url, archive_path, args.build_path,
# "dff2939fd404d054c1036cc0409d19f1")
# shutil.move(os.path.join(args.build_path, "glew-2.1.0"), path)
build_cmake_project(args, os.path.join(path, "build/cmake/__build__"))
if PLATFORM_IS_WINDOWS:
shutil.move(os.path.join(args.install_path, "bin/glew32.dll"),
os.path.join(args.install_path, "lib/glew32.dll"))
os.remove(os.path.join(args.install_path, "bin/glewinfo.exe"))
os.remove(os.path.join(args.install_path, "bin/visualinfo.exe"))
def build_gflags(args):
path = os.path.join(args.build_path, "gflags")
# if os.path.exists(path):
# return
# url = "https://github.com/gflags/gflags/archive/v2.2.1.zip"
# archive_path = os.path.join(args.download_path, "gflags-2.2.1.zip")
# download_zipfile(url, archive_path, args.build_path,
# "2d988ef0b50939fb50ada965dafce96b")
# shutil.move(os.path.join(args.build_path, "gflags-2.2.1"), path)
os.remove(os.path.join(path, "BUILD"))
build_cmake_project(args, os.path.join(path, "__build__"))
def build_glog(args):
path = os.path.join(args.build_path, "glog")
# if os.path.exists(path):
# return
# url = "https://github.com/google/glog/archive/v0.3.5.zip"
# archive_path = os.path.join(args.download_path, "glog-0.3.5.zip")
# download_zipfile(url, archive_path, args.build_path,
# "454766d0124951091c95bad33dafeacd")
# shutil.move(os.path.join(args.build_path, "glog-0.3.5"), path)
build_cmake_project(args, os.path.join(path, "__build__"))
def build_suite_sparse(args):
if not args.with_suite_sparse:
return
path = os.path.join(args.build_path, "suite-sparse")
# if os.path.exists(path):
# return
# url = "https://codeload.github.com/jlblancoc/" \
# "suitesparse-metis-for-windows/zip/" \
# "7bc503bfa2c4f1be9176147d36daf9e18340780a"
# archive_path = os.path.join(args.download_path, "suite-sparse.zip")
# download_zipfile(url, archive_path, args.build_path,
# "e7c27075e8e0afc9d2cf188630090946")
# shutil.move(os.path.join(args.build_path,
# "suitesparse-metis-for-windows-"
# "7bc503bfa2c4f1be9176147d36daf9e18340780a"), path)
build_cmake_project(args, os.path.join(path, "__build__"))
if PLATFORM_IS_WINDOWS:
lapack_blas_path = os.path.join(args.install_path,
"lib64/lapack_blas_windows/*.dll")
for library_path in glob.glob(lapack_blas_path):
copy_file_if_not_exists(
library_path, os.path.join(args.install_path, "lib",
os.path.basename(library_path)))
def build_ceres_solver(args):
path = os.path.join(args.build_path, "ceres-solver")
# if os.path.exists(path):
# return
# url = "https://github.com/ceres-solver/ceres-solver/archive/1.14.0.zip"
# archive_path = os.path.join(args.download_path, "ceres-solver-1.14.0.zip")
# download_zipfile(url, archive_path, args.build_path,
# "26b255b7a9f330bbc1def3b839724a2a")
# shutil.move(os.path.join(args.build_path, "ceres-solver-1.14.0"), path)
extra_config_args = [
"-DBUILD_TESTING=OFF",
"-DBUILD_EXAMPLES=OFF",
]
if args.with_suite_sparse:
extra_config_args.extend([
"-DLAPACK=ON",
"-DSUITESPARSE=ON",
])
if PLATFORM_IS_WINDOWS:
extra_config_args.extend([
"-DLAPACK_LIBRARIES={}".format(
os.path.join(args.install_path,
"lib64/lapack_blas_windows/liblapack.lib")),
"-DBLAS_LIBRARIES={}".format(
os.path.join(args.install_path,
"lib64/lapack_blas_windows/libblas.lib")),
])
if PLATFORM_IS_WINDOWS:
extra_config_args.append("-DCMAKE_CXX_FLAGS=/DGOOGLE_GLOG_DLL_DECL=")
build_cmake_project(args, os.path.join(path, "__build__"),
extra_config_args=extra_config_args)
5.改完上面几个函数后,还有个地方需要更改,就是build_post_process函数,这个函数会复制一些库到__install__目录下,其中对于cgal相关库那一部分的复制需要与你实际安装的版本对应,我的修改如下
def build_post_process(args):
...
if args.cgal_path:
gmp_lib_path = os.path.join(
args.cgal_path, "../auxiliary/gmp/lib/libgmp-10.dll")
if os.path.exists(gmp_lib_path):
copy_file_if_not_exists(
gmp_lib_path,
os.path.join(args.install_path, "lib/libgmp-10.dll"))
copy_file_if_not_exists(
os.path.join(args.cgal_path,
"bin/Release/CGAL-vc140-mt-4.14.3.dll"),
os.path.join(args.install_path, "lib/CGAL-vc140-mt-4.14.3.dll"))
这个地方不改也行,报错的时候把对应的库手动复制过去也行“bin/Release/CGAL-vc140-mt-4.14.3.dll” 和 “lib/CGAL-vc140-mt-4.14.3.dll”类似文件可以在cgal的build目录下找到的
这里补充说明下,eigen库在编译时可能需要用到boost库,但是cmake过程中没有办法让你手动选择,因此可能需要你提前修改eigen的makefile,找到eigen\CMakeLists.txt,在一个比较靠前的位置set一下Boost_INCLUDE_DIR,如下
set(Boost_INCLUDE_DIR "E:/3DAbout/ThirdParty/boost_1_66_0/msvc-140_64/include/boost-1_66")
6.在colmap根目录下打开cmd,使用build.py脚本进行编译即可
python scripts/python/build.py --build_path E:/3DAbout/colmap/build --colmap_path E:/3DAbout/colmap --boost_path "E:/3DAbout/ThirdParty/boost_1_66_0/msvc-140_64" --qt_path "C:/Qt/Qt5.9.3/5.9.3/msvc2015_64" --cuda_path "C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v10.0" --cgal_path "E:/3DAbout/ThirdParty/CGAL-4.14.3/build"
这里建议在colmap根目录下创建一个bat脚本,直接执行bat脚本即可
7.等待…
经过漫长的等待,应该是会编译成功的,不成功,可能需要你具体地去查查哪里有问题了,不过一般按照我这流程下来是没问题的。脚本跑完之后会在build目录下生成__install__目录,里面会有COLMAP.bat,执行即可,至此编译成功。
8.如果你有反复需要编译的需求,可以将build.py中那些第三方库编译函数中的目录检测功能重新打开,防止后面在编译的时候又会编译一遍第三方库,就是这句
if os.path.exists(path):
return
总结:编译完成后发现,其实也没什么难的,windows下的编译问题多发生在那个build.py,哪里发生问题了就去看看build.py那一部分所对应的功能是啥,然后看看能不能曲线解决下,加油,希望你能编译出一个能用的colmap,欢迎大家有问题在评论区讨论。