はじめに
MeshLabにおいて、スクリプトで自動で操作をする記事です。例として、メッシュフィルター(quadric edge collapse decimation)を用いた、remeshingを行います。
作るもの
今回はこの操作をスクリプト(python)で行います。
対象読者
Meshlabの操作を自動化したい人
create python script
開発環境は、下記の通りです。
Ubuntu 16.04
python 3.6
まずは、meshlabをインストールします。
$ sudo apt-get update
$ sudo apt-get install meshlab
meshlabのスクリプトは、meshlabを実際に操作した際に保存されます。そのため、まずは上記gifと同じ操作をmeshlabで直接行います。
その後、メニュータブのFilters->Show current filter scriptを選択します。
そして、先ほど操作したフィルター名が表示されているのを確認して、Save Scriptでどこか適当な場所に保存します。
保存したmlxファイルを見ると、先ほどのフィルタのパラメータ等が記載されています。
<!DOCTYPE FilterScript>
<FilterScript>
<filter name="Quadric Edge Collapse Decimation">
<Param type="RichInt" value="100" name="TargetFaceNum"/>
<Param type="RichFloat" value="0" name="TargetPerc"/>
<Param type="RichFloat" value="0.3" name="QualityThr"/>
<Param type="RichBool" value="false" name="PreserveBoundary"/>
<Param type="RichFloat" value="1" name="BoundaryWeight"/>
<Param type="RichBool" value="false" name="PreserveNormal"/>
<Param type="RichBool" value="false" name="PreserveTopology"/>
<Param type="RichBool" value="true" name="OptimalPlacement"/>
<Param type="RichBool" value="false" name="PlanarQuadric"/>
<Param type="RichBool" value="false" name="QualityWeight"/>
<Param type="RichBool" value="true" name="AutoClean"/>
<Param type="RichBool" value="false" name="Selected"/>
</filter>
</FilterScript>
meshlabではmeshlabserverというコマンドでmeshlabを操作する。
このコマンドの引数で最低限使うのは、
-i [filename…] 開くパス+ファイル名
-o [filename…] 書き出すパス+ファイル名
-s filename 適用するスクリプトのパス+ファイル名
であり、コマンドプロンプトから下記のようなコマンドで実施できる。
meshlabserver -i input.obj -o output.ply -s temp.mlx
なので、複数ファイルを実施する際には、これをpythonスクリプトでfor文等で実施する。
pythonスクリプトは https://gist.github.com/tylerlindell/7435ca2261e7c404ccc1241f18e483aa に先人が作成してくれているので、これを修正して使用してください。
#!/usr/bin/env python
import sys
import os
import subprocess
# Script taken from doing the needed operation
# (Filters > Remeshing, Simplification and Reconstruction >
# Quadric Edge Collapse Decimation, with parameters:
# 0.9 percentage reduction (10%), 0.3 Quality threshold (70%)
# Target number of faces is ignored with those parameters
# conserving face normals, planar simplification and
# post-simplimfication cleaning)
# And going to Filter > Show current filter script
filter_script_mlx = """<!DOCTYPE FilterScript>
<FilterScript>
<filter name="Simplification: Quadric Edge Collapse Decimation (with texture)">
<Param type="RichInt" value="60000" name="TargetFaceNum"/>
<Param type="RichFloat" value="0" name="TargetPerc"/>
<Param type="RichFloat" value="1" name="QualityThr"/>
<Param type="RichBool" value="true" name="PreserveBoundary"/>
<Param type="RichFloat" value="1" name="BoundaryWeight"/>
<Param type="RichBool" value="true" name="OptimalPlacement"/>
<Param type="RichBool" value="true" name="PreserveNormal"/>
<Param type="RichBool" value="true" name="PlanarSimplification"/>
</filter>
</FilterScript>
"""
cwd = os.getcwd()
def create_tmp_filter_file(filename='filter_file_tmp.mlx'):
with open(cwd + 'tmp_' + filename, 'w') as f:
f.write(filter_script_mlx)
return cwd + 'tmp_' + filename
def reduce_faces(in_file, out_file,
filter_script_path=create_tmp_filter_file()):
# Add input mesh
command = "meshlabserver -i " + in_file
# Add the filter script
command += " -s " + filter_script_path
# Add the output filename and output flags
command += " -o " + out_file
# Execute command
print("Going to execute: " + command)
output = subprocess.call(command, shell=True)
last_line = output
print()
print("Done:")
print(in_file + " > " + out_file + ": " + str(last_line))
if __name__ == '__main__':
if len(sys.argv) < 3:
print("Usage:")
print(sys.argv[0] + " /path/to/input_mesh num_iterations")
print("For example, reduce 10 times:")
print(sys.argv[0] + " /home/myuser/mymesh.dae 10")
exit(0)
in_mesh = sys.argv[1]
filename = in_mesh.split('\\')[-1]
num_iterations = int(sys.argv[2])
folder_name = filename.replace('.', '_')
tmp_folder_name = cwd + '\\tmp\\' + folder_name + '_meshes\\'
print("Input mesh: " + in_mesh + " (filename: " + filename + ")")
print("Num iterations: " + str(num_iterations))
print("Output folder: " + tmp_folder_name)
try:
os.mkdir(tmp_folder_name)
except OSError as e:
print(sys.stderr, "Exception creating folder for meshes: " + str(e))
for it in range(num_iterations):
if num_iterations == 1:
out_mesh = tmp_folder_name + folder_name + "_it" + str(it) + ".obj"
reduce_faces(in_mesh, out_mesh)
else:
out_mesh = tmp_folder_name + folder_name + "_it" + str(it) + ".obj"
reduce_faces(last_out_mesh, out_mesh)
last_out_mesh = out_mesh
print()
print("Done reducing, find the files at: " + tmp_folder_name)
おわりに
簡単にでしたが、meshlabにてメッシュのフィルター操作を自動化しました。blenderやその他3DソフトもAPIは豊富にあるので、出来るだけ自動化しましょう。
参考サイト一覧