どうもこんにちは。
管理人のコンです。
会社では普段私は画像を取り扱っているのですが、、、画像を扱わない人と話をしていると
・画像が明るいの?暗いの?
・ピントがあってるの?あってないの?
等かなり感覚的な質問をされます。
これらに対してPythonを使って、画像から統計量を計算していきたいと思います。
今回は「画像明るいの?暗いの?」についてお答えいたします。
お題
今回は以下の2つの画像を使いたいと思います。
この2つの画像は画像処理用の 標準画像データベースSIDBA(Standard Image Data-BAse)というところから使わせてもらいました。このSIDBAの画像データ入手先ですが、比較的入手しやすい神奈川工科大学さんのwebサイトから自分のPCにダウンロードして使わせてもらいました。
まずはWOMAN.bmpを使って統計量を計算していきます。
画像の統計量
ヒストグラム
画像の画素値がどんな値が多いかをみる手法にヒストグラムがあります。
これは横軸に画素値を、縦軸にその頻度(画像中に何個その画素値があるか)を棒グラフで表したものです。
例えば上記の2つの画像だと下記のようになります。
ヒストグラムは、画像の中にどのような画素値がどれくらい含まれているかを可視化したものです。
これでどういう事が分かるかと
例えばWOMAN.bmpは分布が広いため、明るいところから暗いところまで含むような画像ということがわかったり
Text.bpmは明暗が分かれているような画像ということがわかります。
Pythonで書くとこんな感じです。
from PIL import Image
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import math
from matplotlib.colors import Normalize
#女性の画像
im = np.array(Image.open('WOMAN.bmp'))
print('画像の大きさ', im.shape)
im_flat = im.flatten()
print('一次元配列に変換', im_flat.shape)
#テキストの画像
im2 = np.array(Image.open('Text.bmp'))
print('画像の大きさ', im2.shape)
im2_flat = im2.flatten()
print('一次元配列に変換', im2_flat.shape)
fig = plt.figure()
#ax1のインスタンスを作成
ax1 = fig.add_subplot(221)
ax2 = fig.add_subplot(223)
ax3 = fig.add_subplot(222)
ax4 = fig.add_subplot(224)
ax1.axis("off")
ax1.imshow(im, cmap = "gray")
ax3.axis("off")
ax3.imshow(im2, cmap = "gray")
#ヒストグラム
ax2.hist(im_flat, bins = 255, color="gray")
ax4.hist(im2_flat, bins = 255, color="gray")
norm = mpl.colors.Normalize(vmin=0, vmax=255)
#norm = mpl.colors.Normalize(vmin=0, vmax=255)
fig.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap="binary_r"), ax=ax2, orientation='horizontal', label='pixel value')
fig.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap="binary_r"), ax=ax4, orientation='horizontal',label='pixel value')
plt.show()
plt.close()
今回は白黒画像を使いましたが、カラーの画像の場合はRGBそれぞれでヒストグラムをとったりします。
平均値・中央値・最頻値
ヒストグラムまで計算できたら、今度は平均や中央値や最頻値を計算しましょう。
例えば、WOMAN.bmpの画像で行うと以下のようになります。
左から
緑の線が平均(average) :画素値のデータを足して画素数で割ったもの
青の線が中央値(median):データを小さい順に並べたデータのちょうど中央にあるデータ
赤の線が最頻値(mode):最もデータ数が多い画素値
を示してます。
この解析をするPythonコードは以下のようになります。
from PIL import Image
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import math
from matplotlib.colors import Normalize
im = np.array(Image.open('WOMAN.bmp'))
print('画像の大きさ', im.shape)
im_flat = im.flatten()
print('一次元配列に変換', im_flat.shape)
#平均
mean = np.mean(im_flat)
#中央
median = np.median(im_flat)
#最頻値
unique, freq = np.unique(im_flat, return_counts=True) #return_counts=Trueが肝
mode = unique[np.argmax(freq)]
freq_max = np.max(freq)
#分散
var = np.var(im_flat)
print('平均値', mean, '中央値', median, '最頻値', mode, '分散', var)
fig = plt.figure()
#ax1のインスタンスを作成
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
ax1.axis("off")
ax1.imshow(im, cmap = "gray")
ax2.hist(im_flat, bins = 255, color="gray")
y_max=freq_max+10
ax2.set_ylim(0,y_max)
norm = mpl.colors.Normalize(vmin=0, vmax=255)
fig.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap="binary_r"), ax=ax2, orientation='horizontal')
#計算値をヒストグラムに描画
tx = 0.03 # 文字出力位置調整用
ty = 0.91 # 文字出力位置調整用
tt = 0.1 # 文字出力位置調整用
tp = dict( horizontalalignment='left',verticalalignment='bottom',
transform=plt.gca().transAxes, fontsize=11 )
plt.text( tx, ty, f'average {mean:.2f}', **tp, color='green')
plt.text( tx, ty-tt, f'median {median:.2f}', **tp, color='blue')
plt.text( tx, ty-2*tt, f'mode {mode:.2f}', **tp, color='red')
plt.vlines(mean, 0, y_max, color='green', linewidth=1)
plt.vlines(median, 0, y_max, color='blue', linewidth=1)
plt.vlines(mode, 0, y_max, color='red', linewidth=1)
plt.show()
plt.close()
コードには分散も計算してます。
これはノイズ計算に使えたりするのですが、この話はまた別に。。。。
どんな場面でつかうのか
これは私の経験談になるのですが、環境変化を捉えていったりします。
製造ラインだと、現場の判断や新しい商品を作る際にライン周りの物の配置を変更したり
新しい製造機械を導入したりと、色々と写真撮影の環境が変わってきたりします。
もちろんそれに合わせて調整が必要なのですが、導入者がつきっきりで一つの検査場をしっかり維持すること
にリソーセスを注ぎ込むわけにもいかず。。。
そんな時に、このような統計量をプロットしていくことで
撮影環境に変化がある?無い?の変化を人の感覚で判断せず、定量的な値で判断していきます。
おわりに
ここまで読んでいただきありがとうございます。
今後もPythonを使った画像処理の記事をどんどん書かせていただきますので
記事を読んでいただけると幸いです。