画像の作成・読み込み・保存
画像と配列
Scikit-image というパッケージには、色々な画像データが収められています。その中から chelsea (チェルシー) という可愛い「にゃんこ」の画像を読み込んでみましょう。
# PYTHON_NUMPY_IMAGE
# In[1]
import numpy as np
import matplotlib.pyplot as plt
# Scikit-imageから猫の画像データを取得
from skimage.data import chelsea
# 画像表示用のFigureとAxesを作成
fig = plt.figure()
ax = fig.add_subplot(111)
# 配列に画像データを格納
img = chelsea()
# 画像を表示
ax.imshow(img)
matplotlib.axes.Axes.imshow() はデータを画像に変えて表示する関数ですが、元の画像データはどのような形式で記述されているのでしょうか?
print() 関数でデータの大きさと中身を表示してみます。
# In[2]
np.set_printoptions(threshold=10)
print("データの形状:\n{}".format(img.shape))
print(img)
データの形状: (300, 451, 3) [[[143 120 104] [143 120 104] [141 118 102] ... [ 45 27 13] [ 45 27 13] [ 45 27 13]] [[146 123 107] [145 122 106] [143 120 104] ... [ 46 29 13] [ 45 29 13] [ 47 30 14]] [[148 126 112] [147 125 111] [146 122 109] ... [ 48 28 17] [ 49 29 18] [ 50 30 19]] ... [[ 92 58 30] [105 71 43] [132 98 71] ... [172 145 138] [172 145 138] [172 145 138]] [[128 92 60] [139 103 71] [134 95 64] ... [166 142 132] [166 142 132] [167 143 133]] [[139 103 71] [127 88 57] [125 86 53] ... [161 137 127] [161 137 127] [162 138 128]]]
このように、画像データは三次元配列として格納されています。データの意味を理解するために、小さな配列を作成して「配列から画像を生成する」という逆手順を実行してみます。
# In[3]
# FigureとAxesを作成
fig = plt.figure()
ax = fig.add_subplot(111)
# 乱数シードを設定
np.random.seed(0)
# ランダム配列を生成
data_r = np.random.randint(0, 255, (3, 3))
print("data_r:\n{}".format(data_r))
# データを赤単色画像に変換
ax.imshow(data_r, "Reds")
# data_r:
# [[172 47 117]
# [192 67 251]
# [195 103 9]]
imshow() のカラーマップに “Reds” を指定して、配列を赤単色画像に変換しています。
配列の各要素はマス目 (ピクセル) ごとの濃淡を 0 ~ 255 の数値で表しています。
同じようにして、緑単色画像を作成します。
# In[4]
fig = plt.figure()
ax = fig.add_subplot(111)
np.random.seed(1)
data_g = np.random.randint(0, 255, (3, 3))
print("data_g:\n{}".format(data_g))
img = np.array(data_g)
ax.imshow(img, "Greens")
# data_g:
# [[ 37 235 140]
# [ 72 137 203]
# [133 79 192]]
最後に青単色画像です。
# In[5]
fig = plt.figure()
ax = fig.add_subplot(111)
np.random.seed(2)
data_b = np.random.randint(0, 255, (3, 3))
print("data_b:\n{}".format(data_b))
img = np.array(data_b)
ax.imshow(img, "Blues")
# data_b:
# [[168 15 237]
# [ 72 22 43]
# [210 75 104]]
赤 (R)、緑 (G)、青 (B) の 3 原色の組み合わせ (RGB) によって、他の色を作り出すことができます。上で作成した 3 種類の画像データを numpy.dstack() で重ね合わせてみましょう。
# In[6]
# 3枚の画像データを重ねる
data = np.dstack((data_r, data_g, data_b))
print(data)
'''
[[[172 37 168]
[ 47 235 15]
[117 140 237]]
[[192 72 72]
[ 67 137 22]
[251 203 43]]
[[195 133 210]
[103 79 75]
[ 9 192 104]]]
'''
配列の最初の要素 [172 37 168] は左上のピクセルが (赤 172, 緑 37, 青 168) の割合で混ぜ合わされた色であることを意味しています。imshow() に 3 次元配列を渡すと、自動的に色を重ね合わせた画像を表示ます。
# In[7]
fig = plt.figure()
ax = fig.add_subplot(111)
# 画像データを作成
img = np.array(data)
# 画像を表示
ax.imshow(img, vmin=0, vmax=255)
plt.savefig("color_image.png", bbox_inches="tight")
画像を作成:Axes.imshow()
matplotlib.axes.Axes.imshow() の詳細を解説します。この関数を使って、NumPy の配列の各要素をピクセルとする画像を作ることができます。imshow() の最も簡単な使い方は、配列とカラーマップ (cmap) を指定する方法です。この場合、渡した配列によってカラーマップは自動的に正規化されます。
# PYTHON_MATPLOTLIB_IMSHOW
# In[1]
import numpy as np
import matplotlib.pyplot as plt
# FigureとAxesを作成
fig = plt.figure()
ax = fig.add_subplot(111)
# NumPyの配列で色を指定
img = np.array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
# 配列の各要素をピクセルとする画像を表示
ax.imshow(img, "Reds")
カラーマップを自分で正規化したい場合は、vmin と vmax を指定します。
# In[2]
fig = plt.figure()
ax = fig.add_subplot(111)
# NumPyの配列で色を指定
img = np.array([[10, 20, 30],
[40, 50, 60],
[70, 80, 90]])
# 配列の各要素をピクセルとする画像を表示
ax.imshow(img, "Reds", vmin=0, vmax=255)
画像を読み込む:plt.imread()
plt.imread() を使うと、外部の画像ファイルを読み込んで配列に格納することができます。たとえば、カレントディレクトリに BlogCat.jpg というファイルがある場合、次のコードで画像を表示させることができます。
# PYTHON_MATPLOTLIB_IMREAD
# In[1]
# FigureとAxesを作成
fig = plt.figure()
ax = fig.add_subplot(111)
# 画像を読み込んで配列に格納
img = plt.imread("BlogCat.jpg")
# 画像を表示
ax.imshow(img)
画像を保存:Figure.savefig(), plt.savefig()
Figure オブジェクトの savefig() で Matplotlib で作成した図をファイルに出力できます。第 1 引数には保存先のファイル名を指定します。ファイル名には .png, .jpeg, .svg などの拡張子を添えて保存形式を決めます。パスを指定しなければ、ファイルはカレントディレクトリに保存されます。
他にも dpi(解像度)や facecolor(塗り潰しの色)、edgecolor(枠線の色) などを指定することができます。tight_layout に True を渡すと余白を最小限にして出力します。
# PYTHON_MATPLOTLIB_SAVEFIG
# In[1]
import numpy as np
import matplotlib.pyplot as plt
# Figureを設定してAxesを追加
fig = plt.figure(figsize=(6, 6))
ax = fig.add_subplot(111)
# Axes(サブプロット)のタイトルを設定
ax.set_title("Cubic Function", size=16)
# x軸の範囲を設定
ax.set_xlim(-2, 10)
ax.set_ylim(-100, 75)
# xのデータを作成
x = np.arange(-2, 10, 0.1)
# yのデータを作成
f = x**3 - 9*x**2 + 4*x + 1
# 軸ラベルの設定
ax.set_xlabel("x", size=14)
ax.set_ylabel("y", size=14)
# 目盛線(グリッド)の表示
ax.grid()
# データをプロット
ax.plot(x, f, color="darkblue")
# 保存先のファイル名(PNG形式)
fname = "cubic.png"
# ファイルを保存
fig.savefig(fname, dpi=64,
facecolor="lightgray", tight_layout=True)
ファイルに出力されるグラフは次のようになります。
ほとんどの場合、Figure.savefig() の代わりに plt.savefig() を使っても同じように画像を保存できます。ただし、複数の Figure オブジェクトを生成している場合には、plt.savefig() は最新の (つまり最後に生成された) Figure オブジェクトを保存します。また、MATLAB スタイルで記述している場合には、Figure オブジェクトを収める変数が明示されていないので、plt.savefig() で保存してください。
コメント
下記は誤植と思われますので、ご確認ください。
「画像を作成 : Axes.imshow( )」の説明文で、自分で規格したい場合 → 自分で正規化したい場合
訂正しておきました。
ありがとうございます。m(_ _)m
NumPyの「配列の連結・分割」の記事に numpy.dstack( ) や numpy.dsplit( ) の解説を追加していただき、本文の「numpy.dstack( )」にそのリンクを張っていただくとありがたいです。
承知しました。(^^)
さっそく記事の執筆にとりかかります。
たった今見て、NumPyの「配列の連結・分割」の記事が更新されているのを確認いたしました。
さっそくのご対応ありがとうございました。これからも勉強させていただきます。
PYTHON_NUMPY_IMAGE In[2] プログラムの実行結果が、三次元配列の途中の [ 47 30 14]]で終わっていますが、省略記号をうまく使って最後の [162 138 128 ]]] まで書いたほうが誤解がないと思いました。
確かにそうですね。In[2] に
np.set_printoptions(threshold=10)
を追加しておきました。