6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OCIのADBで点群から3Dメッシュ(ポリゴン)を生成し、OBJ形式でエクスポートして可視化してみた。

6
Posted at

はじめに

Oracle Autonomous Database(ADB)のSpatial機能を使い、点群の登録、3Dメッシュの生成、OBJ形式への変換、OCI Object Storageへの出力までをADB内で実行する方法を紹介します。

前提

  • Oracle Autonomous AI Database 26aiを使用できる
  • MDSYS.SDO_PC_PKGMDSYS.SDO_TIN_PKGを実行できる
  • ADBでOracle JVMが有効になっている
  • OCI Object Storageに書き込めるIAMユーザーまたはResource Principalがある

ADB ServerlessでTIN関連パッケージを使うには、Oracle JVMが必要です。
詳細はSDO_TIN_PKGの公式リファレンスを参照してください。

1. サンプル点群を作る

VAL_D1VAL_D2VAL_D3を、それぞれX、Y、Z座標として使用します。

CREATE TABLE point_cloud_input (
    val_d1 NUMBER,
    val_d2 NUMBER,
    val_d3 NUMBER
);

BEGIN
    FOR x IN -4 .. 4 LOOP
        FOR y IN -4 .. 4 LOOP
            INSERT INTO point_cloud_input (val_d1, val_d2, val_d3)
            VALUES (
                x * 0.25,
                y * 0.25,
                0.15 * SIN(x * 0.7) * COS(y * 0.7)
            );
        END LOOP;
    END LOOP;
    COMMIT;
END;
/

image.png

2. SDO_PC点群を作る

点群本体とブロックを格納する表を作ります。

CREATE TABLE point_cloud_models (
    id NUMBER PRIMARY KEY,
    pc MDSYS.SDO_PC
);

CREATE TABLE point_cloud_blocks AS
SELECT *
FROM MDSYS.SDO_PC_BLK_TABLE
WHERE 1 = 0;

入力表から点群を生成します。

BEGIN
    MDSYS.SDO_PC_PKG.CREATE_PC_UNIFIED(
        pc_type        => 'Hilbert R-tree',
        inp_table      => 'POINT_CLOUD_INPUT',
        base_table     => 'POINT_CLOUD_MODELS',
        data_table     => 'POINT_CLOUD_BLOCKS',
        pc_id          => 1,
        pc_tol         => 0.001,
        blk_size       => 10000,
        srid           => NULL,
        create_pyramid => 0
    );
END;
/

入力表の仕様はCREATE_PC_UNIFIEDの公式リファレンスで確認できます。

3. メッシュ生成用の点表を作る

CREATE_MESHESの入力には、VAL_D1VAL_D2VAL_D3を持つ表が必要です。
点群を上からXY平面へ投影し、指定した長方形内にある点だけを表へ取り出します。

BEGIN
    MDSYS.SDO_PC_PKG.CLIP_PC_INTO_TABLE(
        pc_table          => 'POINT_CLOUD_MODELS',
        pc_column         => 'PC',
        id_column         => 'ID',
        id                => '1',
        query             => MDSYS.SDO_GEOMETRY(
                                 2003,
                                 NULL,
                                 NULL,
                                 MDSYS.SDO_ELEM_INFO_ARRAY(1, 1003, 3),
                                 MDSYS.SDO_ORDINATE_ARRAY(-5, -5, 5, 5)
                             ),
        where_clause      => NULL,
        result_table_name => 'PC_MESH_INPUT',
        lods              => MDSYS.SDO_LODS_TYPE(1)
    );
END;
/

4. 3Dメッシュを作る

メッシュ本体とブロックを格納する表を作ります。

CREATE TABLE meshes (
    id NUMBER PRIMARY KEY,
    tin MDSYS.SDO_TIN
);

CREATE TABLE mesh_blocks AS
SELECT *
FROM MDSYS.SDO_TIN_BLK_TABLE
WHERE 1 = 0;

点表から3Dメッシュを生成します。

BEGIN
    MDSYS.SDO_TIN_PKG.CREATE_MESHES(
        base_table      => 'MESHES',
        data_table      => 'MESH_BLOCKS',
        tin_id          => 1,
        tin_tol         => 0.001,
        blk_size        => NULL,
        inptab          => 'PC_MESH_INPUT',
        srid            => NULL,
        feature_size    => 0.75,
        max_angle       => 120,
        blocking        => 'Consecutive-Mesh-Based',
        batch_size_pts  => 100000
    );
END;
/

tin_idMESHES.IDになります。

5. メッシュをOBJへ変換する

Oracle SpatialのメッシュはMESH_BLOCKS内のBLOBに格納されます。
SDO_TIN_PKG.TO_GEOMETRYでブロックをSDO_GEOMETRYへ変換し、OBJの頂点行vと面行fを生成します。

次の表へOBJデータをBLOBとして保存します。

CREATE TABLE mesh_obj_exports (
    export_id  NUMBER PRIMARY KEY,
    tin_id     NUMBER NOT NULL,
    obj_data   BLOB NOT NULL,
    created_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL
);

次は三角形ごとに3頂点を出力する、単純な例です。
共有頂点を重複して出力するためファイルは大きくなりますが、OBJとして可視化できます。

DECLARE
    l_clob         CLOB;
    l_blob         BLOB;
    l_geom         MDSYS.SDO_GEOMETRY;
    l_vertex_no    PLS_INTEGER := 0;
    l_dim          PLS_INTEGER;
    l_start        PLS_INTEGER;
    l_etype        PLS_INTEGER;
    l_interp       PLS_INTEGER;
    l_dest_offset  PLS_INTEGER := 1;
    l_src_offset   PLS_INTEGER := 1;
    l_lang_context PLS_INTEGER := 0;
    l_warning      PLS_INTEGER;

    PROCEDURE append_line(p_text VARCHAR2) IS
    BEGIN
        DBMS_LOB.WRITEAPPEND(l_clob, LENGTH(p_text) + 1, p_text || CHR(10));
    END;

    FUNCTION n(p_value NUMBER) RETURN VARCHAR2 IS
    BEGIN
        RETURN TO_CHAR(
            p_value,
            'TM9',
            'NLS_NUMERIC_CHARACTERS=''.,'''
        );
    END;
BEGIN
    DBMS_LOB.CREATETEMPORARY(l_clob, TRUE);
    append_line('# Exported from Oracle Spatial SDO_TIN');
    append_line('o mesh_1');

    FOR b IN (
        SELECT points, triangles, num_points, num_triangles
        FROM mesh_blocks
        WHERE obj_id = 1
        ORDER BY blk_id
    ) LOOP
        l_geom := MDSYS.SDO_TIN_PKG.TO_GEOMETRY(
            b.points,
            b.triangles,
            b.num_points,
            b.num_triangles,
            3,
            3,
            NULL
        );

        l_dim := TRUNC(l_geom.sdo_gtype / 1000);

        FOR i IN 0 .. TRUNC(l_geom.sdo_elem_info.COUNT / 3) - 1 LOOP
            l_start  := l_geom.sdo_elem_info(i * 3 + 1);
            l_etype  := l_geom.sdo_elem_info(i * 3 + 2);
            l_interp := l_geom.sdo_elem_info(i * 3 + 3);

            IF l_etype IN (1003, 2003) AND l_interp = 1 THEN
                FOR v IN 0 .. 2 LOOP
                    append_line(
                        'v ' ||
                        n(l_geom.sdo_ordinates(l_start + v * l_dim)) || ' ' ||
                        n(l_geom.sdo_ordinates(l_start + v * l_dim + 1)) || ' ' ||
                        n(l_geom.sdo_ordinates(l_start + v * l_dim + 2))
                    );
                END LOOP;

                append_line(
                    'f ' || (l_vertex_no + 1) || ' ' ||
                            (l_vertex_no + 2) || ' ' ||
                            (l_vertex_no + 3)
                );
                l_vertex_no := l_vertex_no + 3;
            END IF;
        END LOOP;
    END LOOP;

    DBMS_LOB.CREATETEMPORARY(l_blob, TRUE);
    DBMS_LOB.CONVERTTOBLOB(
        dest_lob     => l_blob,
        src_clob     => l_clob,
        amount       => DBMS_LOB.LOBMAXSIZE,
        dest_offset  => l_dest_offset,
        src_offset   => l_src_offset,
        blob_csid    => NLS_CHARSET_ID('AL32UTF8'),
        lang_context => l_lang_context,
        warning      => l_warning
    );

    INSERT INTO mesh_obj_exports (export_id, tin_id, obj_data)
    VALUES (1, 1, l_blob);

    COMMIT;
    DBMS_LOB.FREETEMPORARY(l_blob);
    DBMS_LOB.FREETEMPORARY(l_clob);
END;
/

頂点数と面数を確認します。

SELECT export_id,
       tin_id,
       DBMS_LOB.GETLENGTH(obj_data) AS obj_bytes,
       created_at
FROM mesh_obj_exports;

TO_GEOMETRYの仕様は公式リファレンスを参照してください。

6. Object Storage用Credentialを登録する

OCIコンソールで次の順にAuth Tokenを生成します。

右上のプロフィール
  → マイ・プロファイル
  → トークンとキー
  → 認証トークン
  → トークンの生成
BEGIN
    DBMS_CLOUD.CREATE_CREDENTIAL(
        credential_name => 'OBJ_STORE_CRED',
        username        => '<identity-domain>/<oci-user-name>',
        password        => '<oci-auth-token>'
    );
END;
/

7. OBJをObject Storageへ出力する

OBJのBLOBをアップロードします。

DECLARE
    l_obj BLOB;
BEGIN
    SELECT obj_data
    INTO l_obj
    FROM mesh_obj_exports
    WHERE export_id = 1;

    DBMS_CLOUD.PUT_OBJECT(
        credential_name => 'OBJ_STORE_CRED',
        object_uri      =>
            'https://objectstorage.<region>.oraclecloud.com'
            || '/n/<namespace>'
            || '/b/<bucket-name>'
            || '/o/mesh_1.obj',
        contents        => l_obj
    );
END;
/

PUT_OBJECTcontentsにはBLOBを渡します。

8. PCで可視化する

Privateバケットの場合は、OCIコンソールからmesh_1.objをダウンロードします。

MeshLab、Blenderなどの可視化ツールで開きます。

以下は、MeshLabで可視化した例です。

image.png

おわりに

ADBのSpatial機能とDBMS_CLOUDを組み合わせると、点群からOBJを作り、Object Storageへ出力する処理をデータベース内で完結できます。

6
2
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
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?