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}")



0 件のコメント:
コメントを投稿