始める前に、コンピューター ビジョンの従来の座標系を理解することが重要です。これに Open3D と Microsoft Kinect センサーが続きます。コンピューター ビジョンでは、画像は別の 2D 座標系で表されます。X 軸は左から右を指し、Y 軸は上下を指します。カメラの場合、3D 座標系の原点はカメラの焦点にあり、x 軸は右を指し、y 軸は下を指し、z 軸は前方を指します。
コンピュータ ビジョン座標系
最初に必要な Python ライブラリをインポートします:
import numpy as np import open3d as o3d
理解を助けるために、PLY ファイルから始めましょう。 Open3D を使用してデフォルトの 3D 座標系を作成し、表示します。
# Read point cloud: pcd = o3d.io.read_point_cloud("data/depth_2_pcd.ply") # Create a 3D coordinate system: origin = o3d.geometry.TriangleMesh.create_coordinate_frame(size=0.5) # geometries to draw: geometries = [pcd, origin] # Visualize: o3d.visualization.draw_geometries(geometries)
座標系を使用して、点群を表示します。 origin
青い矢印は Z 軸、赤い矢印は X 軸、緑の矢印は Y 軸です。点群が Open3D 座標系と同じ座標系で表されていることがわかります。次に、各軸の最小値と最大値を持つ点を取得しましょう。
# Get max and min points of each axis x, y and z: x_max = max(pcd.points, key=lambda x: x[0]) y_max = max(pcd.points, key=lambda x: x[1]) z_max = max(pcd.points, key=lambda x: x[2]) x_min = min(pcd.points, key=lambda x: x[0]) y_min = min(pcd.points, key=lambda x: x[1]) z_min = min(pcd.points, key=lambda x: x[2])
それらは印刷できますが、視覚的にわかりやすくするために、各点の位置に球を作成します。デフォルトでは、Open3D は原点位置に 3D ジオメトリを作成します。
球を指定の位置に移動するには、平行移動変換が必要です。以下の例では、球はベクトル [1,1,1] によって変換されます。
例に戻り、各球に色を割り当ててみましょう。位置ごとに球を作成し、それをその位置に移動します。次に、正しい色を割り当て、最後にそれをディスプレイに追加します。
# Colors: RED = [1., 0., 0.] GREEN = [0., 1., 0.] BLUE = [0., 0., 1.] YELLOW = [1., 1., 0.] MAGENTA = [1., 0., 1.] CYAN = [0., 1., 1.] positions = [x_max, y_max, z_max, x_min, y_min, z_min] colors = [RED, GREEN, BLUE, MAGENTA, YELLOW, CYAN] for i in range(len(positions)): # Create a sphere mesh: sphere = o3d.geometry.TriangleMesh.create_sphere(radius=0.05) # move to the point position: sphere.translate(np.asarray(positions[i])) # add color: sphere.paint_uniform_color(np.asarray(colors[i])) # compute normals for vertices or faces: sphere.compute_vertex_normals() # add to geometry list to display later: geometries.append(sphere) # Display: o3d.visualization.draw_geometries(geometries)
実際、Y 軸は点の高さを表します。現実の世界では、最も高いボールは黄色のボールで、最も低いボールは緑です。ボール。ただし、y 軸は下向きであるため、黄色の球の値が最小となり、緑の球の値が最大になります。
もう 1 つの興味深い球は、原点にあるシアンの球です。前のチュートリアルで説明したように、深度値 0 のピクセルはノイズ ポイントであるため、原点に位置する点は、これらのノイズ ピクセルから計算された点になります (z=0 の場合、x=0、y= 0)。
いくつかの重要なポイントを示しましたが、地面検出を実行するにはどうすればよいでしょうか? 前の例では、緑色の球体が地面上にあります。正確に言うと、その中心は、地上点である y 軸に沿った最高点に対応します。地面検出のために、y_max を持つすべての点の色を緑に変更するとします。
点群を表示すると、すべての地面点が緑色であるわけではないことがわかります。実際、前の緑色の球の中心に対応する 1 つの点だけが緑色です。これは、深度カメラの精度とノイズが原因です。
この制限を克服するには、y 座標が [y_max-threshold, y_max] の点が地上点とみなされるように、しきい値を追加する必要があります。これを行うには、y_max を取得した後、各点の y 座標が間隔内にあるかどうかを確認し、その色を緑色に設定します。最後に、点群の色属性が更新され、結果が表示されます。
# Define a threshold: THRESHOLD = 0.075 # Get the max value along the y-axis: y_max = max(pcd.points, key=lambda x: x[1])[1] # Get the original points color to be updated: pcd_colors = np.asarray(pcd.colors) # Number of points: n_points = pcd_colors.shape[0] # update color: for i in range(n_points): # if the current point is aground point: if pcd.points[i][1] >= y_max - THRESHOLD: pcd_colors[i] = GREEN# color it green pcd.colors = o3d.utility.Vector3dVector(pcd_colors) # Display: o3d.visualization.draw_geometries([pcd, origin])
この例では、地面を表す点のみを緑色に着色します。実際のアプリケーションでは、ロボットや視覚障害者システムなどの歩行可能なエリアを定義したり、インテリア デザイン システムなどその上にオブジェクトを配置したりするために地面が抽出されます。これは削除することもできるので、シーン理解システムや物体検出システムのように、残りのポイントをセグメント化または分類することができます。
点群は 3D 点のセットとして定義されることがわかっています。集合は順序付けされていない構造であるため、集合によって表される点群は非組織化点群と呼ばれます。 RGB マトリックスと同様に、組織化された点群は、点の x、y、z 座標を表す 3 つのチャネルを持つ 2D マトリックスです。マトリックス構造は隣接する点間の関係を提供するため、最近傍アルゴリズムなどの一部のアルゴリズムの時間の複雑さが軽減されます。
たとえば、研究論文を書いていて、検出アルゴリズムの結果をグラフの形式で表示したいと考えています。以下に示すように、点群のスクリーンショットを撮ることも、結果を深度画像に表示することもできます。私の意見では、2 番目のオプションが最善です。この場合、深度ピクセルの位置を保存するには、整理された点群が必要です。
左:3D 可视化的屏幕截图 右:深度图像的结果
让我们从之前的深度图像创建一个有组织的点云。我们首先导入相机参数。我们还导入深度图像并将其转换为3通道灰度图像,以便我们可以将地面像素设置为绿色:
import imageio.v3 as iio import numpy as np import matplotlib.pyplot as plt # Camera parameters: FX_DEPTH = 5.8262448167737955e+02 FY_DEPTH = 5.8269103270988637e+02 CX_DEPTH = 3.1304475870804731e+02 CY_DEPTH = 2.3844389626620386e+02 # Read depth image: depth_image = iio.imread('../data/depth_2.png') # Compute the grayscale image: depth_grayscale = np.array(256 * depth_image / 0x0fff, dtype=np.uint8) # Convert a grayscale image to a 3-channel image: depth_grayscale = np.stack((depth_grayscale,) * 3, axis=-1)
要计算一个有组织的点云,我们使用与上一篇教程相同的方法(Python:基于 RGB-D 图像的点云计算)。我们没有将深度图像扁平化,而是将jj和ii重塑为与深度图像相同的形状,如下所示:
# get depth image resolution: height, width = depth_image.shape # compute indices and reshape it to have the same shape as the depth image: jj = np.tile(range(width), height).reshape((height, width)) ii = np.repeat(range(height), width).reshape((height, width)) # Compute constants: xx = (jj - CX_DEPTH) / FX_DEPTH yy = (ii - CY_DEPTH) / FY_DEPTH # compute organised point cloud: organized_pcd = np.dstack((xx * depth_image, yy * depth_image, depth_image))
如果你打印出创建的点云的形状,你可以看到它是一个有3个通道的矩阵(480,640,3)。如果你觉得这个代码很难理解,请回到之前的教程(Python:基于 RGB-D 图像的点云计算)。
类似地,我们像上面那样检测地面,但不是更新点的颜色并显示点云,而是更新灰度图像的像素并显示它:
# Ground_detection: THRESHOLD = 0.075 * 1000# Define a threshold y_max = max(organized_pcd.reshape((height * width, 3)), key=lambda x: x[1])[ 1]# Get the max value along the y-axis # Set the ground pixels to green: for i in range(height): for j in range(width): if organized_pcd[i][j][1] >= y_max - THRESHOLD: depth_grayscale[i][j] = [0, 255, 0]# Update the depth image # Display depth_grayscale: plt.imshow(depth_grayscale) plt.show()
在本教程中,为了熟悉点云,我们引入了默认坐标系统,并实现了一个简单的地面检测算法。事实上,地面检测在某些应用(如导航)中是一项重要的任务,文献中已经提出了几种算法。实现算法简单;它认为最低点是地面。然而,它的限制是,深度相机必须与地面平行,这是大多数现实应用的情况不是这样的。
以上がPython を使用して点群地面検出を実装するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。