はじめに
Oracle Autonomous Database(ADB)のSpatial機能を使い、点群の登録、3Dメッシュの生成、OBJ形式への変換、OCI Object Storageへの出力までをADB内で実行する方法を紹介します。
前提
- Oracle Autonomous AI Database 26aiを使用できる
-
MDSYS.SDO_PC_PKGとMDSYS.SDO_TIN_PKGを実行できる - ADBでOracle JVMが有効になっている
- OCI Object Storageに書き込めるIAMユーザーまたはResource Principalがある
ADB ServerlessでTIN関連パッケージを使うには、Oracle JVMが必要です。
詳細はSDO_TIN_PKGの公式リファレンスを参照してください。
1. サンプル点群を作る
VAL_D1、VAL_D2、VAL_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;
/
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_D1、VAL_D2、VAL_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_idはMESHES.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_OBJECTのcontentsにはBLOBを渡します。
8. PCで可視化する
Privateバケットの場合は、OCIコンソールからmesh_1.objをダウンロードします。
MeshLab、Blenderなどの可視化ツールで開きます。
以下は、MeshLabで可視化した例です。
おわりに
ADBのSpatial機能とDBMS_CLOUDを組み合わせると、点群からOBJを作り、Object Storageへ出力する処理をデータベース内で完結できます。

