はじめに
matplotlibで0が含まれ画像を対数(log)スケールで表示するとき、以下の図の左側のように0の値が白塗りされてしまう事態に遭遇したことはないだろうか。0値にカラーをつけるなら、右側のような色になってほしいですよね。
そこで、本記事では、白塗りにならないで正しく表示する方法を紹介します。
実装
Google Colabで作成した本記事のコードは、こちらにあります。
各種インポート
import cv2
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal
from scipy.signal import convolve
実行時の各種バージョン
Name | Version |
---|---|
numpy | 1.21.6 |
matplotlib | 3.2.2 |
scipy | 1.7.3 |
サンプル画像
def sample_img(px=25, sigma=1):
half_px = px // 2
x, y = np.mgrid[-half_px:half_px+1:, -half_px:half_px+1:]
pos = np.dstack((x, y))
mean = np.array([0, 0])
cov = np.array([[sigma**2, 0], [0, sigma**2]])
rv = multivariate_normal(mean, cov)
img = rv.pdf(pos)
img[img < 1e-10] = 0 # 本記事用に小さい値を0に置換しておく
return img
ガウシアンを使います。ただし、本記事では0値がほしいので適当に1e-10
より小さい値を0
に置換しています。
まずはそのままで見てみよう
img = sample_img()
fig, ax = plt.subplots()
cbar = ax.imshow(img, norm=colors.LogNorm())
fig.colorbar(cbar)
plt.show()
0値が白塗りされているのがわかります。
方法
白塗りになる原因は、単純にlogスケールで0を表現できないためです。そのため、0値をカラーバーの最小値に置換すれば解決できます。ここでは、カラーバーの最小値を設定しない場合と設定した場合の両方のケースで白塗りにならない表示方法を紹介します。
カラーバーの範囲がオートの場合
img = sample_img()
# 1番小さい値(0)を2番目に小さい値に置換
second_min = np.unique(img)[1]
mask_img = np.copy(img)
mask_img[img < second_min] = second_min
fig, ax = plt.subplots()
cbar = ax.imshow(mask_img, norm=colors.LogNorm())
fig.colorbar(cbar)
plt.show()
説明
- np.unique()を使うことで、画像の値を重複を除き昇順でソートされます。
- その中には、0値が最小値として含まれていて、その一つ大きい値(
np.unique(img)[1]
)は0以外なので、その値をsecond_min
に代入します。 -
mask_img[img < second_min] = second_min
で、second_min
より小さい値をsecond_min
に置換します。 - 後は、
colors.LogNorm())
とすることで、オートでmask_img
の最小値・最大値でカラーをつけてくれます。
カラーバーの範囲を任意で指定する場合
img = sample_img()
# color_minに任意の範囲を指定
color_min = 1e-6
mask_img = np.copy(img)
mask_img[img < color_min] = color_min
fig, ax = plt.subplots()
cbar = ax.imshow(mask_img, norm=colors.LogNorm(vmin=color_min, vmax=None))
fig.colorbar(cbar)
plt.show()
説明
-
color_min
にカラーバーの最小値を指定します。 -
mask_img[img < color_min] = color_min
で、color_min
より小さい値をcolor_min
に置換します。 - 後は、
colors.LogNorm(vmin=color_min, vmax=None)
とすることで、最小値の範囲を指定しカラーをつけてくれます。(vmax
には、None
でなく、適当な値を入れることもできます。)
まとめ
matplotlibで、0値をlogスケールにしたいけど、白塗りになってしまうという問題を解決する方法を紹介しました。
np.unique()
は、0が暗黙に含まれて、カラーバーをオートでつけるときに役に立つのでぜひ使ってみてください。
また、単純にカラーの最小値を指定する場合は、やはり、普通にその最小値になるように置換するのが良いかなと思います。
matplotlibで0が含まれた画像の対数表示は、少し慎重に置換しながら解決していきましょう。
参考資料