ヒートマップで画像に面白表現!【Python】

プログラミング・数学・英会話
スポンサーリンク

画像にヒットマップを重ねたいんだけどヒートマップ画像だけの生成方法の解説ページしかない!画像解析結果をわかりやすく見える化してみたい!ヒートマップを描画する方法を知りたい!そんな方は本記事を読んでください

本記事ではPythonでmatplotlibによるヒートマップを使って画像に面白表現を行う方法について紹介します

スポンサーリンク

対象読者

本ページでは、pythonの基本やpipなどによるインストール方法などはご存知の方を想定しています

何かご不明点があれば、遠慮なくコメント等でご質問ください

必要なライブラリのインストール

まずは下記のコマンドで最低限必要なライブラリをインストールしましょう

pip install matplotlib numpy scipy</code></pre> <!-- /wp:code -->  <!-- wp:paragraph --> これだけで大丈夫です <!-- /wp:paragraph -->  <!-- wp:heading --> <h2>ヒートマップ画像の作成方法</h2> <!-- /wp:heading -->  <!-- wp:paragraph --> まずは2次元ガウス(正規)分布を使って中心から徐々に値が下がっていくような2次元メッシュデータを作成します <!-- /wp:paragraph -->  <!-- wp:paragraph --> 縦横がX, Yとしたときの2次元ガウス分布の値を高さで図時すると、下記のように平均点が一番高く平均点から離れるにつれて徐々に低くなるような分布です <!-- /wp:paragraph -->  <!-- wp:image {"align":"center","id":269,"sizeSlug":"medium"} --> <div class="wp-block-image"><figure class="aligncenter size-medium"><img src="https://mathematical-free.com/wp-content/uploads/2020/07/9c4b6c41b880358302ecae7eb6ec7b4c-300x280.jpg" alt="" class="wp-image-269"/></figure></div> <!-- /wp:image -->  <!-- wp:html --> ガウス分布:Z = f(X, Y)$としてscipy.statsのmultivariate_normalという関数を用いて下記のように計算することができます



import numpy as np
from scipy.stats import multivariate_normal
import matplotlib.pyplot as plt

heat_w, heat_h = (200, 200)
X, Y = np.mgrid[0:heat_w:200j, 0:heat_h:200j]
pos = np.dstack((X, Y))
mu = np.array([heat_w//2, heat_h//2])
sigma = np.array([[1000, 10], [10, 1000]])
lmd = 10000
Z = lmd * multivariate_normal(mu, sigma).pdf(pos)

それではまず、先ほどの図のように3次元でプロットしてみましょう

from mpl_toolkits.mplot3d import axes3d

fig = plt.figure(figsize = (15, 15))
axes = fig.add_subplot(111, projection='3d')

axes.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='jet')
plt.show()

先ほどの図が表示されているかと思います

※ jupyter notebookなどを用いている場合は、%matplotlib inlineをライブラリインポートのあとに追加してください

cmapは、color mapと言われるもので、ヒートマップとして表示するカラーマップの種類を指定します

../../_images/sphx_glr_colormaps_007.png
引用元:https://matplotlib.org/3.1.0/tutorials/colors/colormaps.html

続いて2次元画像として表示してみましょう

plt.imshow(Z, cmap='jet')

(100, 100)を中心とするヒートマップ画像を作ることができました

画像にヒートマップを重ねてみよう

それでは次の画像の宝箱の部分にヒートマップを重ねてみましょう

# 画像を読み込む
img = plt.imread('takara.jpg')

# 画像が大きいのでリサイズ
height = 300
width = 200
img = cv2.resize(img, (width, height))

# 重ねるheatmap用の上記画像の同サイズのnumpy配列作成
heatmap_img = np.zeros((height, width))

# ヒートマップの中心点
cx, cy = (90, 200)

# 先ほど計算したZ値をheatmap_imgの(cx, cy)を中心に重ねる処理
for i in range(heat_h):
    for j in range(heat_w):
        if(cy - heat_h//2 + i < 0) or (cy - heat_h//2 + i > height - 1):
            continue
        if(cx - heat_w//2 + j < 0) or (cx - heat_w//2 + j > width - 1):
            continue
        heatmap_img[cy - heat_h//2 + i, cx - heat_w//2 + j] = min(255, heatmap_img[cy - heat_h//2 + i, cx - heat_w//2 + j] + Z[i, j])

# まずは画像を表示
plt.imshow(img)

# 画像の上にheatmap_imgを透過率0.4で重ねる
plt.imshow(heatmap_img, cmap='jet', alpha=0.4)

# 表示する
plt.show()

うまく宝箱に重なりました!

今回は単純に宝箱の上にヒートマップを重ねてみましたが、実際にはある条件にしたがってヒットマップを複数重ねてみるなど様々な表現が可能です

例えば画像解析などの結果をわかりやすく表示するためにヒートマップを使ったりもできます

どんな使い方でも今回の応用でできますので、いろいろな活用方法を考えて使ってみてください!

面白い使い方をした方はコメントで報告してもらえると嬉しいです

matplotlibの出力を余白、軸情報なしに画像として保存する方法

最後におまけですが、上記の画像をメモリや余白なしで、サイズを指定して画像保存する方法を記載します

# 画像を読み込む
img = plt.imread('takara.jpg')

# 画像が大きいのでリサイズ
height = 300
width = 200
img = cv2.resize(img, (width, height))

# 重ねるheatmap用の上記画像の同サイズのnumpy配列作成
heatmap_img = np.zeros((height, width))

# ヒートマップの中心点
cx, cy = (90, 200)

# 先ほど計算したZ値をheatmap_imgの(cx, cy)を中心に重ねる処理
for i in range(heat_h):
    for j in range(heat_w):
        if(cy - heat_h//2 + i < 0) or (cy - heat_h//2 + i > height - 1):
            continue
        if(cx - heat_w//2 + j < 0) or (cx - heat_w//2 + j > width - 1):
            continue
        heatmap_img[cy - heat_h//2 + i, cx - heat_w//2 + j] = min(255, heatmap_img[cy - heat_h//2 + i, cx - heat_w//2 + j] + Z[i, j])

# まずは画像を表示
plt.imshow(img)

# 画像の上にheatmap_imgを透過率0.4で重ねる
fig = plt.imshow(heatmap_img, cmap='jet', alpha=0.4)

# いらないものを消す
fig.axes.get_xaxis().set_visible(False)
fig.axes.get_yaxis().set_visible(False)
plt.axis(False)

# 画像を保存する
plt.savefig('test.jpg', bbox_inches='tight', pad_inches=0)

# 画像を表示する
plt.show()

上記のコードを見てみてわからない点などがあればご質問ください!

ここでの内容は画像はもちろん、カメラ映像や動画に対しても適用できます

カメラや動画周りの処理を知りたい方はこちら!

コメント

Copied title and URL