LoginSignup
9
8

More than 3 years have passed since last update.

Meshlab スクリプトによるメッシュフィルター操作を自動化する方法

Last updated at Posted at 2019-06-25

はじめに

MeshLabにおいて、スクリプトで自動で操作をする記事です。例として、メッシュフィルター(quadric edge collapse decimation)を用いた、remeshingを行います。

作るもの

meshlab.gif

今回はこの操作をスクリプト(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を選択します。
tem.png

そして、先ほど操作したフィルター名が表示されているのを確認して、Save Scriptでどこか適当な場所に保存します。
tem.png

保存したmlxファイルを見ると、先ほどのフィルタのパラメータ等が記載されています。

temp.mxl
<!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は豊富にあるので、出来るだけ自動化しましょう。

参考サイト一覧

9
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8