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

0 件のコメント:

コメントを投稿