ラベル Pythonスクリプト の投稿を表示しています。 すべての投稿を表示
ラベル Pythonスクリプト の投稿を表示しています。 すべての投稿を表示

2026年9月3日木曜日

技術メモ GeoPackageの中身を覗くPythonスクリプト

 Technical Note: Python Script to Inspect GeoPackage Contents


I created a Python script to inspect the contents of GeoPackage files (.gpkg) used in QGIS. When dealing with multiple GeoPackage files, this allows you to check the tables stored within them via the command line, without having to open each file individually in QGIS.


QGISで使うGeoPackageファイル(.gpkg)の中身を覗くPythonスクリプトを作成しました。GeoPackageファイルが多数ある場合、それらを一つ一つQGISで開かなくても、コマンドラインでGeoPackageファイルに格納されているテーブルを確認できます。

1 GeoPackageファイルに格納されている全てのテーブルを確認するPythonスクリプト

GeoPackageファイルに格納されている全てのテーブルの確認をコマンドラインで確認できるPythonスクリプトを作成しました。QGISで作成したテーブルだけでなく、SQLiteの管理用テーブルも含まれます。


GeoPackageファイルに格納されている全てのテーブルを確認している様子

2 GeoPackageファイルに格納されているQGIS関連テーブルを確認するPythonスクリプト

GeoPackageファイルに格納されているQGIS関連テーブルだけの確認をコマンドラインで確認できるPythonスクリプトを作成しました。SQLiteの管理用テーブルは含まれません。


GeoPackageファイルに格納されているQGIS関連テーブルを確認している様子

3 QGISPythonコンソールでGeoPackageファイルに格納されているQGIS関連テーブルと詳細情報を表示するPythonスクリプト

QGISPythonコンソールでGeoPackageファイルに格納されているQGIS関連テーブルと詳細情報を表示するPythonスクリプトを作成しました。詳細情報はレイヤ名・ジオメトリ型・CRS・フィールド名・型・地物数を一覧表として表示します。


QGISPythonコンソールでGeoPackageファイルの詳細情報を表示している様子

ただし、一覧表示でなければ、このPythonスクリプトで表示される情報はすべて通常のQGIS操作で確認できる情報です。

4 3のPythonスクリプトをコマンドラインで走らせるPythonスクリプト

3のPythonスクリプトをコマンドラインで走らせるPythonスクリプトを作成しました。


3のPythonスクリプトをコマンドラインで走らせるPythonスクリプトの様子

但し、現状では作業中のため、当該Pythonスクリプトと対象GeoPackageファイル(.gpkg)の双方のフルパス入力が必要なので、使い勝手が悪いです。

5 感想

GeoPackageファイルの内容はQGISにインストールすれば、通常操作で全て判ります。しかし、類似作業が増え、多数フォルダーに類似GeoPackageファイルが存在するようになった場合、それらを一つ一つQGISで確認することは非効率的です。多数GeoPackageファイルの確認に特に2のPythonスクリプトの存在意義が高まります。


2026年6月22日月曜日

技術メモ pngファイルからヒートマップを作成するPythonスクリプトの改良

 Technical Notes: Improvements to a Python Script for Creating Heatmaps from PNG Files


I've been using a very convenient Python script to create heatmaps from dot distribution images (transparent background PNG files). However, I encountered some issues, so I've improved it. The resulting Python script is now more robust.


ドット分布画像(背景透明pngファイル)からヒートマップを作成するPythonスクリプトをとても便利に使っています。しかし、不都合が生じたので改良しました。より堅牢なPythonスクリプトとなりました。

この記事は2026.06.03記事「技術メモ pngファイルから直接ヒートマップを作成するPythonスクリプト」の続きになります。

1 遭遇した不都合


遭遇した不都合

これまで順調にpngファイルからPythonスクリプトで直接ヒートマップを作成してきました。しかし、上記のような不都合に遭遇しました。

調べたところ、画像の透明部分にRGBの色が残っている部分が存在しているために生じた現象であることがわかりました。Pythonスクリプトは透明か否かではなく、色がある部分=点群データとして扱っているので生じた不都合です。

2 不都合の解決策

この不都合の解決策として、次の2を執りました。

1 画像透明部から色を完全に除去する新たなPythonスクリプト作成。

2 色のある透明部を間違って拾わないヒートマップ作成Pythonスクリプトの作成。

それぞれのPythonスクリプトを作成しましたが、2の新たな改良ヒートマップ作成Pythonスクリプトを使う方が作業の手間がはぶけます。ヒートマップ作成Pythonスクリプトがより堅牢となりました。

3 改良Pythonスクリプト (透明部に色が残っていても正常に作動する改良版)


from pathlib import Path

import cv2
import numpy as np
from PIL import Image
from matplotlib import cm

# =========================
# 設定
# =========================

input_path = r"G:/test/aaa.png"
output_path = r"G:/test/aaa_heatmap.png"

# 使用する単色グラデーション
# 例: "Blues", "Reds", "Greens", "Purples", "Oranges", "Greys"
color_map_name = "Reds"

# グラデーションの区分数
# 例: 5, 8, 10, 16, 32
gradient_steps = 10

# 密度計算のぼかし半径
# 大きいほど広くなめらかな密集度になる
blur_radius = 20

# 背景を透明にするか
transparent_background = False

# 点群抽出に使うアルファ閾値
# alpha > alpha_threshold のピクセルだけを点として扱う
# 0: わずかでも不透明なら点
# 10〜30: ほぼ透明なノイズを除外
alpha_threshold = 20

# ヒートマップの透明出力時、薄い密度部分を透明にする閾値
# 通常は 0.0 のままでOK
heat_alpha_threshold = 0.0

# =========================
# 処理
# =========================

input_file = Path(input_path)
if not input_file.exists():
    raise FileNotFoundError(f"画像が読み込めません: {input_path}")

# RGBAで読み込み、RGB値には依存しない
img = Image.open(input_file).convert("RGBA")
arr = np.array(img)

# アルファチャンネルだけで点群を抽出
alpha = arr[:, :, 3]
dot_mask = alpha > alpha_threshold

point_pixels = int(np.count_nonzero(dot_mask))
if point_pixels == 0:
    raise ValueError(
        "アルファ値から点を検出できませんでした。"
        f" alpha_threshold={alpha_threshold} を下げてください。"
    )

print(f"画像サイズ: {img.size[0]} x {img.size[1]}")
print(f"alpha min/max: {int(alpha.min())} / {int(alpha.max())}")
print(f"点として使うピクセル数: {point_pixels}")

# 密度画像を作成
density = dot_mask.astype(np.float32)

# ガウシアンぼかしで密集度を計算
density = cv2.GaussianBlur(
    density,
    ksize=(0, 0),
    sigmaX=blur_radius,
    sigmaY=blur_radius,
)

# 0〜1に正規化
max_density = float(density.max())
if max_density > 0:
    density = density / max_density

# グラデーションを段階化
density_step = np.floor(density * gradient_steps) / gradient_steps
density_step = np.clip(density_step, 0, 1)

# カラーマップ適用
cmap = cm.get_cmap(color_map_name)
heat_rgba = cmap(density_step)

# 0〜255へ変換
heat_img = (heat_rgba[:, :, :3] * 255).astype(np.uint8)

# 密度ゼロ部分の処理
if transparent_background:
    out_alpha = (density > heat_alpha_threshold).astype(np.uint8) * 255
    output = np.dstack([heat_img, out_alpha])
    Image.fromarray(output, mode="RGBA").save(output_path)
else:
    # 背景は白
    heat_img[density <= heat_alpha_threshold] = [255, 255, 255]
    Image.fromarray(heat_img, mode="RGB").save(output_path)

print(f"ヒートマップを書き出しました: {output_path}")

2026年6月3日水曜日

技術メモ pngファイルから直接ヒートマップを作成するPythonスクリプト

 Technical Memo: Python Script to Create Heatmaps Directly from PNG Files


I created a Python script that directly outputs heatmaps from dot distribution images (PNG files). Previously, I used QGIS functions to create heatmaps, but this significantly improves work efficiency.


ドット分布画像(pngファイル)から直接ヒートマップをアウトプットするPythonスクリプトを作成しました。これまではQGIS機能を利用してヒートマップを作成していたのですが、それと較べると著しく作業効率が向上しました。

1 Pythonスクリプトによるヒートマップ作成


利用画像(ドット分布画像、背景が透明なpng画像)

ドット分布画像を用意します。背景が白地の場合は背景が透明なpngファイルにします。

ドットの色の面積をpixel単位に計測して、それによりヒートマップを作成するので、ドットが重なってつぶれていても、データは近似的に利用できます。


ヒートマップ

単色グラデーションによるヒートマップを作成します。

ヒートマップ作成は次の項目を変更して望みのイメージに合うまで試行できます。

色、グラデーションの区分、ぼかし半径


ヒートマップとドット分布図のオーバーレイ画像

2 使い勝手

これまではQGIS機能を利用してヒートマップを作成していたのですが、Pythonを使うと著しく作業効率が向上しました。

ドット分布図のドットが重なってつぶれているような場合でも、データとして近似的に利用できるので、ヒートマップの趣旨(分布概要をざっくり把握する)とも合います。

3 Pythonスクリプト


import cv2
import numpy as np
from PIL import Image
from matplotlib import cm

# =========================
# 設定
# =========================

# ドット分布図のパスとアウトプットのパス→変更してください
input_path = r"G:/test/aaa.png"
output_path = r"G:/test/aaa_heatmap.png"

# 使用する単色グラデーション
# 例: "Blues", "Reds", "Greens", "Purples", "Oranges", "Greys"
color_map_name = "Purples"

# グラデーションの区分数
# 例: 5, 8, 10, 16, 32
gradient_steps = 10

# 密度計算のぼかし半径
# 大きいほど広くなめらかな密集度になる
blur_radius = 50

# 背景を透明にするか
transparent_background = False

# =========================
# 処理
# =========================

# 画像読み込み
img = cv2.imread(input_path, cv2.IMREAD_COLOR)
if img is None:
    raise FileNotFoundError(f"画像が読み込めません: {input_path}")

# RGBへ変換
rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# 黒背景以外の点を抽出
# 青い点など、黒でないピクセルを遺物ドットとみなす
gray = cv2.cvtColor(rgb, cv2.COLOR_RGB2GRAY)
dot_mask = gray > 1

# 密度画像を作成
density = dot_mask.astype(np.float32)

# ガウシアンぼかしで密集度を計算
density = cv2.GaussianBlur(
    density,
    ksize=(0, 0),
    sigmaX=blur_radius,
    sigmaY=blur_radius
)

# 0〜1に正規化
if density.max() > 0:
    density = density / density.max()

# グラデーションを段階化
density_step = np.floor(density * gradient_steps) / gradient_steps
density_step = np.clip(density_step, 0, 1)

# カラーマップ適用
cmap = cm.get_cmap(color_map_name)
heat_rgba = cmap(density_step)

# 0〜255へ変換
heat_img = (heat_rgba[:, :, :3] * 255).astype(np.uint8)

# 密度ゼロ部分の処理
if transparent_background:
    alpha = (density > 0).astype(np.uint8) * 255
    output = np.dstack([heat_img, alpha])
    Image.fromarray(output, mode="RGBA").save(output_path)
else:
    # 背景は白
    heat_img[density <= 0] = [255, 255, 255]
    Image.fromarray(heat_img, mode="RGB").save(output_path)

print(f"ヒートマップを書き出しました: {output_path}")

2023年6月10日土曜日

Blenderでカラーグラデーションを塗るPythonスクリプト

 Python script to paint color gradient in Blender


As part of learning BlenderPython, I created a Python script that paints a color gradient. For me, this activity is a follow-up activity, but I think I can expect further development and application in the future.


BlenderPython学習の一環としてカラーグラデーションを塗るPythonスクリプトを作成しました。見よう見まねの活動ですが、今後発展応用が期待できそうです。

1 平面メッシュを生成してそれにカラーグラデーションを塗るBlenderPythonスクリプト

……………………………………………………………………

# 平面メッシュにノードを設定してカラーグラデーションを塗る

# bpyのインポート

import bpy

# 平面メッシュ生成とマテリアル設定、ノードを使用、ブレンドモードをアルファブレンドに設定

bpy.ops.mesh.primitive_plane_add()

obj = bpy.context.object

mat = bpy.data.materials.new(name="rainbow")

obj.active_material = mat

mat.use_nodes = True

mat.blend_method = "BLEND"

# テクスチャ座標設定

ndtc = mat.node_tree.nodes.new("ShaderNodeTexCoord")

ndtc.location = -800, 250

# マッピング設定

ndmp = mat.node_tree.nodes.new("ShaderNodeMapping")

ndmp.location = -630, 250

# グラデーションテクスチャ設定

ndgr = mat.node_tree.nodes.new("ShaderNodeTexGradient")

ndgr.location = -450, 250

# カラーランプ設定

ndvr = mat.node_tree.nodes.new("ShaderNodeValToRGB")

ndvr.location = -260, 250

ndvr.color_ramp.color_mode = "RGB"

ndvr.color_ramp.interpolation = "LINEAR"

ndvr.color_ramp.elements[0].position = 0.239

ndvr.color_ramp.elements[0].color = 1, 0, 0, 1

p1 = ndvr.color_ramp.elements.new(position=0.565)

p1.color = 0, 1, 0, 1

ndvr.color_ramp.elements[2].position = 0.792

ndvr.color_ramp.elements[2].color = 0.04, 0.015, 1, 1

# プリンシプルBSDF設定

ndpb = mat.node_tree.nodes["Principled BSDF"]

ndpb.inputs[0].default_value = 0, 0, 0, 1

ndpb.inputs[7].default_value = 0

ndpb.inputs[9].default_value = 1

ndpb.inputs[17].default_value = 0

ndpb.inputs[18].default_value = 1

# ノード間のリンク設定

mat.node_tree.links.new(ndtc.outputs[0], ndmp.inputs[0])

mat.node_tree.links.new(ndmp.outputs[0], ndgr.inputs[0])

mat.node_tree.links.new(ndgr.outputs[0], ndvr.inputs[0])

mat.node_tree.links.new(ndvr.outputs[0], ndpb.inputs[0])


……………………………………………………………………


Blender画面(Scriptingにこのスクリプトをコピーして走らせた結果)

2 感想

2-1 感想 1

ノード設定をPythonスクリプトで書く方法の基本を理解しましたので、カラーグラデーションに限らずマテリアルやテクスチャ設定を伴うメッシュオブジェクトをPythonスクリプトで記述できるようになると思います。従って、逆にPythonスクリプトを使って効率的に特定のマテリアルやテクスチャ設定を伴うメッシュオブジェクト生成が出来るようになります。

2-2 感想 2

やまだこーじさんが開発した干渉色表現をカラーランプで行い、それをPythonスクリプトで記述すれば、それを使ってPythonスクリプトでメッシュオブジェトに干渉色を塗ることが出来るようになります。ガリー侵食地形面など考古的地形面とか遺物とかに干渉色を塗ることにより、地形や遺物の形態的特徴を効果的に表現できることが可能になります

3 追記

1で作成したBlenderPythonスクリプトを種に、虹色7色(赤~燈~黄~緑~青~藍~紫)のカラーグラデーションで平面メッシュを塗るPythonスクリプトをChatGPTに作ってもらいました。適切なスクリプトができました。

この質問と回答から、カラーランプの細かい作業を手作業でしなくても、Pythonスクリプトを使って質問することで、その面倒な作業をChatGPTに代行させることが可能であると確認しました。

……………………………………………………………………

【ChatGPT質問】

次のBlenderPythonスクリプトはBlenderで新たな平面メッシュを生成して、それにノードを設定することにより、赤~緑~青のカラーグラデーションを塗るものです。

このスクリプトを参考にして、新たな平面メッシュに赤(1,0,0,1)~燈(1,0.5,0,1)~黄(1,1,0,1)~緑(0,1,0,1)~青(0,0,1,1)~藍(0.5,0,1,1)~紫(1,0,0.5,1)をカラーグラデーションで塗るPythonスクリプトを教えてください。

……………………………………………………………………

# 平面メッシュにノードを設定してカラーグラデーションを塗る

# bpyのインポート

import bpy

# 平面メッシュ生成とマテリアル設定、ノードを使用、ブレンドモードをアルファブレンドに設定

bpy.ops.mesh.primitive_plane_add()

obj = bpy.context.object

mat = bpy.data.materials.new(name="rainbow")

obj.active_material = mat

mat.use_nodes = True

mat.blend_method = "BLEND"

# テクスチャ座標設定

ndtc = mat.node_tree.nodes.new("ShaderNodeTexCoord")

ndtc.location = -800, 250

# マッピング設定

ndmp = mat.node_tree.nodes.new("ShaderNodeMapping")

ndmp.location = -630, 250

# グラデーションテクスチャ設定

ndgr = mat.node_tree.nodes.new("ShaderNodeTexGradient")

ndgr.location = -450, 250

# カラーランプ設定

ndvr = mat.node_tree.nodes.new("ShaderNodeValToRGB")

ndvr.location = -260, 250

ndvr.color_ramp.color_mode = "RGB"

ndvr.color_ramp.interpolation = "LINEAR"

ndvr.color_ramp.elements[0].position = 0.239

ndvr.color_ramp.elements[0].color = 1, 0, 0, 1

p1 = ndvr.color_ramp.elements.new(position=0.565)

p1.color = 0, 1, 0, 1

ndvr.color_ramp.elements[2].position = 0.792

ndvr.color_ramp.elements[2].color = 0.04, 0.015, 1, 1

# プリンシプルBSDF設定

ndpb = mat.node_tree.nodes["Principled BSDF"]

ndpb.inputs[0].default_value = 0, 0, 0, 1

ndpb.inputs[7].default_value = 0

ndpb.inputs[9].default_value = 1

ndpb.inputs[17].default_value = 0

ndpb.inputs[18].default_value = 1

# ノード間のリンク設定

mat.node_tree.links.new(ndtc.outputs[0], ndmp.inputs[0])

mat.node_tree.links.new(ndmp.outputs[0], ndgr.inputs[0])

mat.node_tree.links.new(ndgr.outputs[0], ndvr.inputs[0])

mat.node_tree.links.new(ndvr.outputs[0], ndpb.inputs[0])

……………………………………………………………………

【ChatGPT回答】

以下は、Blenderで新たな平面メッシュに赤~燈~黄~緑~青~藍~紫のカラーグラデーションを塗るPythonスクリプトの例です。

……………………………………………………………………

# bpyのインポート

import bpy

# 平面メッシュ生成とマテリアル設定、ノードを使用、ブレンドモードをアルファブレンドに設定

bpy.ops.mesh.primitive_plane_add()

obj = bpy.context.object

mat = bpy.data.materials.new(name="rainbow")

obj.active_material = mat

mat.use_nodes = True

mat.blend_method = "BLEND"

# テクスチャ座標設定

ndtc = mat.node_tree.nodes.new("ShaderNodeTexCoord")

ndtc.location = -800, 250

# マッピング設定

ndmp = mat.node_tree.nodes.new("ShaderNodeMapping")

ndmp.location = -630, 250

# グラデーションテクスチャ設定

ndgr = mat.node_tree.nodes.new("ShaderNodeTexGradient")

ndgr.location = -450, 250

# カラーランプ設定

ndvr = mat.node_tree.nodes.new("ShaderNodeValToRGB")

ndvr.location = -260, 250

ndvr.color_ramp.color_mode = "RGB"

ndvr.color_ramp.interpolation = "LINEAR"

# 赤~燈~黄~緑~青~藍~紫のカラーグラデーションを設定

ndvr.color_ramp.elements[0].position = 0.143

ndvr.color_ramp.elements[0].color = 1, 0, 0, 1

ndvr.color_ramp.elements.new(position=0.286).color = 1, 0.5, 0, 1

ndvr.color_ramp.elements.new(position=0.429).color = 1, 1, 0, 1

ndvr.color_ramp.elements.new(position=0.571).color = 0, 1, 0, 1

ndvr.color_ramp.elements.new(position=0.714).color = 0, 0, 1, 1

ndvr.color_ramp.elements.new(position=0.857).color = 0.5, 0, 1, 1

ndvr.color_ramp.elements.new(position=1).color = 1, 0, 0.5, 1

# プリンシプルBSDF設定

ndpb = mat.node_tree.nodes["Principled BSDF"]

ndpb.inputs[0].default_value = 0, 0, 0, 1

ndpb.inputs[7].default_value = 0

ndpb.inputs[9].default_value = 1

ndpb.inputs[17].default_value = 0

ndpb.inputs[18].default_value = 1

# ノード間のリンク設定

mat.node_tree.links.new(ndtc.outputs[0], ndmp.inputs[0])

mat.node_tree.links.new(ndmp.outputs[0], ndgr.inputs[0])

mat.node_tree.links.new(ndgr.outputs[0], ndvr.inputs[0])

mat.node_tree.links.new(ndvr.outputs[0], ndpb.inputs[0])

……………………………………………………………………

このスクリプトでは、カラーランプの要素のposition(位置)とcolor(色)を調整して、赤~燈~黄~緑~青~藍~紫のカラーグラデーションを作成しています。

……………………………………………………………………


ChatGPT回答スクリプトを走らせて得られたBlender画面


2023年6月4日日曜日

Blenderでメッシュ平面図を表現するPythonスクリプト

 Python script to represent mesh floor plan in Blender


When examining the 3D distribution of archaeological artifacts in Blender, it becomes necessary to create a related 2D distribution map (mesh diagram). So I made a Python script to express the mesh plan in Blender with the help of ChatGPT. Convenient.


Blenderで考古遺物の3D分布を検討する際に、関連して2D分布図(メッシュ図)を作成する必要が生まれます。そこでBlenderでメッシュ平面図を表現するPythonスクリプトをChatGPTの助けを借りて作りました。便利です。

1 2D分布図(メッシュ図)をBlenderで作成する必要性

有吉北貝塚北斜面貝層では全ての遺物についてそれが出土した2m×2mメッシュ番号が記録されていて、土器以外の遺物はすべて出土メッシュ番号が発掘調査報告書に記載されています。従って出土遺物のメッシュ別分布図を作成することができます。これまでの作業では遺物のメッシュ別分布図はデータをExcelで集計してQGISで表現してきています。


QGISで表現したメッシュ別分布図を使った資料

しかし、3D分布検討はBlenderで行い、2D分布検討はQGISで行うという作業は複雑であり好ましくありません。全遺物の3D分布検討を視野にいれたとき、ぜひとも2D分布図(メッシュ図)もBlenderで作成し、3D分布検討と2D分布検討を同じBlenderで行うことが大切になります。

2 2D分布図(メッシュ図)をBlenderで作成するPythonスクリプト

2-1 ChatGPTに質問

ChatGPTに次の質問をしました。

……………………………………………………………………

次の操作を行うBlenderPythonスクリプトを教えてください。

1 Blenderで既に選択してあるメッシュオブジェクトが1つあります。

2 そのメッシュオブジェクトを最初に多数コピーして、それを別の座標に次々に配置します。

メッシュオブジェクトを配置したい座標は、テキストファイル'C:/tmp/XYZData.txt'に(任意記号,x,y,z)の順番で書かれています。

3 アウトライナーのシーンコレクションに新しいコレクションを作成します。

4 新しく作成したコレクションに、先ほど座標に配置した多数メッシュオブジェクトを格納します。

……………………………………………………………………

2-1 ChatGPTからの回答

ChatGPTから次の回答をもらいました。

……………………………………………………………………

import bpy

# テキストファイルのパス

filepath = 'C:/tmp/XYZData.txt'

# メッシュオブジェクトの名前

object_name = bpy.context.active_object.name

# テキストファイルを読み込む

with open(filepath, 'r') as file:

    lines = file.readlines()

# アクティブなシーンを取得する

scene = bpy.context.scene

# 新しいコレクションを作成する

new_collection = bpy.data.collections.new('NewCollection')

scene.collection.children.link(new_collection)

# 行ごとに処理

for line in lines:

    # カンマで分割してデータを取得

    data = line.strip().split(',')

    symbol, x, y, z = data[0], float(data[1]), float(data[2]), float(data[3])

   # メッシュオブジェクトをコピーする

    bpy.ops.object.select_all(action='DESELECT')

    bpy.data.objects[object_name].select_set(True)

    bpy.context.view_layer.objects.active = bpy.data.objects[object_name]

    bpy.ops.object.duplicate(linked=False)

    new_object = bpy.context.active_object

   # コピーしたオブジェクトの座標を設定する

    new_object.location = (x, y, z)

    # コピーしたオブジェクトを新しいコレクションに追加する

    new_collection.objects.link(new_object)

    # オブジェクトの名前を設定する

    new_object.name = symbol

……………………………………………………………………

このスクリプトでは、指定されたテキストファイルのパスをfilepath変数に、Blenderのアクティブなオブジェクトの名前をobject_name変数に指定してください。

スクリプトを実行すると、指定されたテキストファイルから座標データを読み取り、メッシュオブジェクトをコピーして指定された座標に配置します。コピーされたオブジェクトの名前は、テキストファイルの記号と対応付けられます。

また、スクリプトでは新しいコレクションを作成し、コピーしたオブジェクトをそのコレクションに格納します。新しいコレクションの名前は"NewCollection"ですが、必要に応じて変更してください。

同様に、このスクリプトもBlender内で実行する必要があります。BlenderのPythonスクリプトエディタやPythonコンソールなどから実行してください。また、スクリプトを実行する前に、Blender内でメッシュオブジェクトを選択しておく必要があります。

……………………………………………………………………


ChatGPTの回答

2-3 ChatGPT回答のPythonスクリプトの結果

次のようなメッシュ番号と3D座標位置を書いたテキストを用意しました。


用意した座標テキスト

次に、2m×2mメッシュの大きさの平面メッシュオブジェクト(マテリアルは青色)を事前にBlender画面内に作成して選択しました。

次にChatGPT教授のPythonスクリプトをScripting画面に貼り付けて走らせました。

その結果所定のメッシュに青色平面が貼り付きました。この例は前期土器出土メッシュ分布図です。


作成された2D分布図(メッシュ図)


作成された2D分布図(メッシュ図)斜め表示

Blenderのアウトライナーを見ると、新たに配置された平面メッシュオブジェクトにメッシュ番号が付き、それらが新コレクションに格納されています。


アウトライナーの様子

自分が望んだPythonスクリプトを入手することができました。

3 今後の展開

このPythonスクリプトでは平面メッシュだけでなく、厚さのある立方体オブジェクトや枠型形状オブジェクトでも同じ操作が出来ました。(土器や遺物そのもののオブジェクトでも出来ると思います。)

このPythonスクリプトを工夫すればメッシュ別に数量を立方体の高さで表現することも可能になります。つぎのようなメッシュ別数量の立体表現もPythonスクリプトで時間をかけないで作成するところまで射程に入りました。


過去に手作業で作成したメッシュ別数量の立体表現

Blenderで3D分布分析、2D分布分析を統合的に行うための大きな基礎が出来ました。もうQGISの助けを借りる必要はありません。

4 感想

4-1 感想 1

Pythonスクリプト作成で、ChatGPTに質問して適切な回答を得るコツが少し判ってきたような気がします。自分はそのスクリプトを書けないけれども、論理的な展開はこれこれだと判った上でそれを丁寧に説明すれば、正解を得られる確率が高まるようです。

4-2 感想 2

ChatGPTから自分ができない(判らない)ことを即座に教えてもらって、大いに助かっているのですが、そのChatGPTは無料で、24時間営業であることに一抹で虚弱な違和感・不安定感を感じます。そしてなによりもChatGPTに気遣いする必要がないことに不思議と戸惑いを感じます。通常、先生・指導者とか同僚・友人知人に物事を教えてもらうときは、教えてもらったことが役立っても役立たなくても何らかのお礼を、少なくても言葉レベルでは、します。

4-3 感想 3

人から何かを教えてもらうときは、「こんなレベルの低いことを教わるのは恥ずかしい」とか「自分が調べもしないで、相手に教わるのは相手に失礼だ」とか、自分と相手の人間関係に起因する様々な感情が自分に生起します。だれでもそうした感情をどれだけ意識するかは別にして必ず持ちます。そうした感情がChatGPT利用時にふと脳裏をよぎりました。それがお門違いであることはよくわかります。ChatGPTと自分との感情的関係が自分にはまだ整理されていません。


2023年5月30日火曜日

遺物分布図から平面座標を取得するPythonスクリプト

 Python script to get plane coordinates from relic distribution map


I created a Python script to obtain the plane coordinates from the relic distribution map (1/10 actual survey map) created at the site. I created a simple image coordinate acquisition script by asking ChatGPT. ChatGPT is useful for Python beginners.


現場で作成された遺物分布図(1/10実測図)から平面座標を取得するPythonスクリプトを作成しました。単純な画像座標取得スクリプトを素にChatGPTに質問して作成しました。Python初心者にとってChatGPTは有用です。

1 遺物分布図(1/10)から取得したい遺物平面座標

次のような画像に含まれる特定メッシュ遺物分布図に記録されている遺物ポイントの座標を取得したいのです。


遺物分布図の画像

2 画像の座標を取得するPythonスクリプト

これまでのPython学習で画像においてマウスをクリックした場所の座標取得Pythonスクリプトを作って使っています。

画像の座標を取得するPythonスクリプト

……………………………………………………………………

import cv2

def onMouse(event, x, y, flags, params):

    if event == cv2.EVENT_LBUTTONDOWN:

       print(x,y)

img = cv2.imread('b6i.png')

cv2.namedWindow('keisokuh22', cv2.WINDOW_NORMAL)

cv2.imshow('keisokuh22', img)

cv2.setMouseCallback('keisokuh22', onMouse)

cv2.waitKey(0)

……………………………………………………………………

3 平面座標取得Pythonスクリプトの作成

次図にあるように、遺物分布図メッシュの四隅の座標は既知ですから、知りたい座標と対向四隅2座標の3個所の画像座標がわかれば単純な計算で平面座標を取得できます。


画像座標3点(未知1点、既知2点)から未知点の平面座標を求める原理

2で作成した画像座標取得Pythonスクリプトを拡張すれば簡単に平面座標取得Pythonスクリプトをつくることができるはずです。

しかし、悲しいかな、そのPythonスクリプト拡張記述が判りません。丸1日web検索などで類似事例を探して試みましたが出来そうでできません。初心者のつらい状況です。その時、ChatGPTなら教えてくれるに違いないと気が付きました。そして、確かにその通りとなりました。次はその質問と回答の様子です。

……………………………………………………………………

【ChatGPTに質問】

次のPythonスクリプト(例1)では画像(b6i.png)の任意点をマウスでクリックするとその座標(x.y)を取得します。

……………………………………………………………………

#Pythonスクリプト(例1)

import cv2

def onMouse(event, x, y, flags, params):

    if event == cv2.EVENT_LBUTTONDOWN:

       print(x,y)

img = cv2.imread('b6i.png')

cv2.namedWindow('keisokuh22', cv2.WINDOW_NORMAL)

cv2.imshow('keisokuh22', img)

cv2.setMouseCallback('keisokuh22', onMouse)

cv2.waitKey(0)

……………………………………………………………………

このPythonスクリプト(例1)を使って、最初にマウスでクリックして取得した座標(x1,y1)と2番目にマウスでクリックして取得した座標(x2,y2)と3番目にマウスでクリックして取得した座標(x3,y3)について、2*(y3-y1)/(y2-y1)の値と 2*(x3-x1)/(x2-x1)の値を求めるPythonスクリプトを教えてください。


【ChatGPTからの回答】

次のようなPythonスクリプトを使用して、指定された計算を行うことができます。

……………………………………………………………………

import cv2


# クリック座標を保存するリスト

click_points = []


def onMouse(event, x, y, flags, params):

    if event == cv2.EVENT_LBUTTONDOWN:

        click_points.append((x, y))

        if len(click_points) >= 3:

            # 計算式の値を求める

            x1, y1 = click_points[0]

            x2, y2 = click_points[1]

            x3, y3 = click_points[2]

            result1 = 2 * (y3 - y1) / (y2 - y1)

            result2 = 2 * (x3 - x1) / (x2 - x1)

            print("2*(y3-y1)/(y2-y1):", result1)

            print("2*(x3-x1)/(x2-x1):", result2)


img = cv2.imread('b6i.png')

cv2.namedWindow('keisokuh22', cv2.WINDOW_NORMAL)

cv2.imshow('keisokuh22', img)

cv2.setMouseCallback('keisokuh22', onMouse)

cv2.waitKey(0)

……………………………………………………………………

このスクリプトでは、クリックされた座標を click_points リストに保存します。リストに3つ以上の座標が保存された時点で、指定された計算を実行し、結果を表示します。


ChatGPTやりとり画面

4 平面座標取得Pythonスクリプトの動作

ChatGPTに教えてもらったPythonスクリプトで求めたい平面座標を取得することができます。


平面座標を取得した様子(コマンドプロンプト画面)

今後このスクリプトを、小数点桁の表示など幾つかの点で、改良して自分好みに使い勝手をよくしていきたいと思います。

5 感想

判らないPythonスクリプトをChatGPTが教えてくれるという状況は、自分にとって「革命的」です。ChatGPTが存在しない過去では、一番の近道はPythonに詳しい人に聞くことです。それができないので自分は(あるいはほとんど全ての人が)苦労して延々と時間をかけて調べるしかないのです。ChatGPTを使うことにより、自分のPython学習・活用が急加速すると予感します。