Open3Dでメモリあり座標軸を描画してみた[Python]

三次元点群処理

Open3Dを使って点群を可視化する際、座標軸が分かりにくいことがあります。そこで、座標軸およびXスケール(目盛り)を追加し、より分かりやすい3D表示を実現する方法を紹介します。

サンプルコード

import numpy as np
import open3d as o3d

def create_axis(scale=1.0, tick_interval=0.2, tick_length=0.1):
    """ XYZ軸と目盛りを作成する """
    points = []
    lines = []
    colors = []

    # XYZ軸の定義 (X: 赤, Y: 緑, Z: 青)
    axis_points = [
        [0, 0, 0], [scale, 0, 0],  # X軸
        [0, 0, 0], [0, scale, 0],  # Y軸
        [0, 0, 0], [0, 0, scale]   # Z軸
    ]
    axis_colors = [
        [1, 0, 0], # X軸 → 赤
        [0, 1, 0], # Y軸 → 緑
        [0, 0, 1], # Z軸 → 青
    ]

    points.extend(axis_points)
    lines.extend([[i, i + 1] for i in range(0, len(axis_points), 2)])
    colors.extend(axis_colors)

    # 目盛りを追加
    for i in np.arange(0, scale + tick_interval, tick_interval):
        if i == 0:
            continue  # 原点の目盛りは不要

        # X軸の目盛り(赤)
        points.append([i, 0, 0]) # 始点(X軸上)
        points.append([i, tick_length, 0]) # 終点
        lines.append([len(points) - 2, len(points) - 1]) # 直前に登録した二点を線として登録
        colors.append([1, 0, 0])  # 赤

        # Y軸の目盛り(緑)
        points.append([0, i, 0]) # 始点(Y軸上)
        points.append([tick_length, i, 0]) # 終点
        lines.append([len(points) - 2, len(points) - 1]) # 直前に登録した二点を線として登録
        colors.append([0, 1, 0])  # 緑

        # Z軸の目盛り(青)
        points.append([0, 0, i]) # 始点(Z軸上)
        points.append([tick_length, 0, i]) # 終点
        lines.append([len(points) - 2, len(points) - 1]) # 直前に登録した二点を線として登録
        colors.append([0, 0, 1])  # 青

    # Open3DのLineSetオブジェクトを作成
    line_set = o3d.geometry.LineSet()
    line_set.points = o3d.utility.Vector3dVector(points)
    line_set.lines = o3d.utility.Vector2iVector(lines)
    line_set.colors = o3d.utility.Vector3dVector(colors)
    
    return line_set


# 軸と目盛りを作成
axis = create_axis(scale=1.0, tick_interval=0.2, tick_length=0.05)
# 描画
o3d.visualization.draw_geometries([axis])

上記のサンプルコードの実行結果が以下です。

サンプルコードの深堀と解説

import numpy as np
import open3d as o3d

def create_axis(scale=1.0, tick_interval=0.2, tick_length=0.1):
   
    points = []  # 座標点を格納するリスト
    lines = []   # 線の接続情報を格納するリスト
    colors = []  # 線の色を格納するリスト
  • 必要なライブラリをインポート
  • 関数を定義する
    • scale (デフォルト: 1.0)
      • XYZ軸の長さを指定します。
      • 例えば scale=2.0 にすると、X, Y, Z軸が 2.0 の長さになります。
      • 点群のスケールに合わせて調整してください。
    • tick_interval (デフォルト: 0.2)
      • 目盛りを打つ間隔を指定します。
      • 例えば tick_interval=0.5 にすると、0.5 の間隔で目盛りが表示されます。
      • scale よりも大きい値にすると、目盛りが表示されません。
    • tick_length (デフォルト: 0.1)
      • 目盛りの長さを指定します。
      • 例えば tick_length=0.05 にすると、目盛りが短くなります。
      • tick_length=0.2 などにすると、より視認性が高くなります。
  • 描画する線分情報を格納するリストを準備する
    • points:座標点を格納するリスト
    • lines:線の接続情報を格納するリスト
    • colors:線の色を格納するリスト

    # XYZ軸の定義 (X: 赤, Y: 緑, Z: 青)
    axis_points = [
        [0, 0, 0], [scale, 0, 0],  # X軸
        [0, 0, 0], [0, scale, 0],  # Y軸
        [0, 0, 0], [0, 0, scale]   # Z軸
    ]
    axis_colors = [
        [1, 0, 0], # X軸 → 赤
        [0, 1, 0], # Y軸 → 緑
        [0, 0, 1], # Z軸 → 青
    ]

    points.extend(axis_points)
    lines.extend([[i, i + 1] for i in range(0, len(axis_points), 2)])
    colors.extend(axis_colors)
  • axis_points:各軸の始点(原点)と終点(スケール値の位置)を定義します。
    • [0, 0, 0], [scale, 0, 0] → X軸は原点 (0,0,0) から (scale,0,0) まで伸びる。
    • [0, 0, 0], [0, scale, 0] → Y軸は原点 (0,0,0) から (0,scale,0) まで伸びる。
    • [0, 0, 0], [0, 0, scale] → Z軸は原点 (0,0,0) から (0,0,scale) まで伸びる。
  • axis_colors では、各軸に対応する色をRGBで指定します。
    • X軸は [1, 0, 0] → 赤色。
    • Y軸は [0, 1, 0] → 緑色。
    • Z軸は [0, 0, 1] → 青色。
  • points.extend(axis_points)
    座標リスト pointsaxis_points の情報を追加。
  • lines.extend([[i, i + 1] for i in range(0, len(axis_points), 2)])
    lines には、始点と終点のインデックスを格納し、線を描画できるようにする
  • colors.extend(axis_colors)
    軸の色を colors に追加。
# 目盛りを追加
    for i in np.arange(0, scale + tick_interval, tick_interval):
        if i == 0:
            continue  # 原点の目盛りは不要
  • for i in np.arange(0, scale + tick_interval, tick_interval)
    • この行は「0 から scale までの間に、tick_interval ごとに区切った値を順番に i に代入していく」ためのループ
    • たとえば scale=1.0tick_interval=0.2 の場合、
      np.arange(0, 1.0 + 0.2, 0.2)
      となり、生成される値は
      [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]
      となり、iに代入される
  • 原点ではメモリ描画は不要なため、i=0 のとき、飛ばして次のfot文へ進む
# X軸の目盛り(赤)
        points.append([i, 0, 0]) # 始点(X軸上)
        points.append([i, tick_length, 0]) # 終点
        lines.append([len(points) - 2, len(points) - 1]) # 直前に登録した二点を線として登録
        colors.append([1, 0, 0])  # 赤
  • points.append([i, 0, 0]):X軸上にメモリの始点を登録
  • points.append([i, tick_length, 0]):直前に登録した始点からtick_length(メモリの長さ)分離れた位置にメモリの終点を登録
  • lines.append([len(points) – 2, len(points) – 1]) :直前に登録した二点を線として登録
  • colors.append([1, 0, 0]):メモリ線の色を赤色に指定する
# Y軸の目盛り(緑)
        points.append([0, i, 0]) # 始点(Y軸上)
        points.append([tick_length, i, 0]) # 終点
        lines.append([len(points) - 2, len(points) - 1]) # 直前に登録した二点を線として登録
        colors.append([0, 1, 0])  # 緑

        # Z軸の目盛り(青)
        points.append([0, 0, i]) # 始点(Z軸上)
        points.append([tick_length, 0, i]) # 終点
        lines.append([len(points) - 2, len(points) - 1]) # 直前に登録した二点を線として登録
        colors.append([0, 0, 1])  # 青
  • X軸上のメモリ線と同様に、Y・Z軸上のメモリも追加していく
  • for文が繰り返すごとに、原点側から+側にメモリ線が追加される
# Open3DのLineSetオブジェクトを作成
    line_set = o3d.geometry.LineSet()
    line_set.points = o3d.utility.Vector3dVector(points)
    line_set.lines = o3d.utility.Vector2iVector(lines)
    line_set.colors = o3d.utility.Vector3dVector(colors)
    
    return line_set
  • line_set = o3d.geometry.LineSet()
    空のLineSetオブジェクトを、line_setとして作成
  • line_set.points = o3d.utility.Vector3dVector(points)
    • 座標点リストをOpen3D形式に変換してセットする
    • Vector3dVector は「3次元の点の集まり」をOpen3D用に変換するラッパー
  • line_set.lines = o3d.utility.Vector2iVector(lines)
    • 線分情報をOpen3D形式に変換してセット
    • 2次元の整数配列(Vector2iVector) に変換
  • line_set.colors = o3d.utility.Vector3dVector(colors)
    • 各線に対応する色情報をセット
    • Pythonのリストを Vector3dVector に変換してOpen3Dに渡す
  • return line_set
    create_axis関数の戻り値としてline_setを設定
# 軸と目盛りを作成
axis = create_axis(scale=1.0, tick_interval=0.2, tick_length=0.05)
# 描画
o3d.visualization.draw_geometries([axis])

ここでは、実際に定義したcreate_axis関数を使用し、座標軸を描画します。

描画結果は以下になります。

まとめ

Open3Dでメモリあり座標軸を描画してみました。関数化し、引数として「座標軸の長さ」「メモリを描画する間隔」「メモリの長さ」を指定可能にすることで、より汎用的に使用できるようにしました。

メモリあり座標軸を描画したかった背景として、物体を同次変換したときに三次元空間上で正確に変換されていることを視覚的に確認したかったことがあります。同次変換の記事は以下ですのでご参考まで。

三次元点群処理は二次元の画像処理よりも情報が少なく、手探りで書籍や論文、公式ドキュメントを調査する日々です。

その中で、導入しやすいPythonとOpen3Dで三次元点群処理を体系的にまとめた書籍が以下になります。現在数少ない三次元点群処理の書籍ですので私も重宝しています。是非参考にしてみてください。

参考リンク

タイトルとURLをコピーしました