画像の作成・読み込み・保存
画像と配列
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)
を追加しておきました。