はじめに
画像処理で明るさやコントラストを変換させる手法として、ガンマ補正があります。
式としては簡単で入力をx,出力をyとすると
$y=255・(\frac{x}{255}) ^ \frac{1}{\gamma}$
で表すことができます。この$\gamma$の値を変化させると、出力画像のピクセルの値を変化させることができます。
$\gamma$が1より大きいと全体的に明るくなり、$\gamma$が1より小さいと全体的に暗くなります。
下の図に$\gamma$の値を変化させたときの入力と出力の関係を示します。
式中の$\frac{1}{\gamma}$が単なる$\gamma$になっている場合もあります。その場合は上に凸と下に凸が入れ替わります。
画像処理ライブラリのOpenCVにも、ガンマ補正の機能があります。
式を見ればわかるように非常に簡単なので、numpyのみでガンマ補正の関数を作ることができます。
*あくまで勉強用
実際にやってみる
式をもとにガンマ補正の関数を作ってみます。一瞬。
# python version 3.6.5
import numpy as np #version 1.14.3
import matplotlib.pyplot as plt
from skimage import io #scikit image version 0.13.1
plt.rcParams["font.family"] = "Times New Roman"
plt.rcParams["font.size"] = 12
def gammma(x, r):
"""
ガンマ補正y=255*(x/255)
x 入力画像
r ガンマ補正の係数
"""
x = np.float64(x)
y = x/255.
y = y **(1/r)
return np.uint8(255*y)
RGB毎にヒストグラムを作成、表示する関数も作成しておきます。(なんたる車輪の再発 …以下略)
def hist_rgb(img):
#rgbのヒストグラムを作成する関数
#結果を格納する変数res [brightness, channel]
res = np.zeros([256, 3])
for channel in range(3):
#あるchannelを抽出
img_tmp = img[:,:,channel]
#画像を1次元にする
img_tmp =img_tmp.reshape(img_tmp.size)
for i in img_tmp:
res[i, channel] += 1
return res
def mat_hist_rgb(hist, ylim = 0.06):
#hist_rgbのfunctionで計算したヒストグラムを表示
x = np.arange(hist.shape[0])
#ヒストグラムの色を指定
colors = ["red", "green", "blue"]
for i, color in enumerate(colors):
plt.bar(x,hist[:, i], color=color, alpha=0.3, width=1.0)
plt.xlabel("Brightness")
plt.ylabel("Frequency")
plt.xlim(0, 255)
plt.yticks([])
plt.show()
まずはlenaさんを読み込んでoriginal画像のヒストグラムを表示させます。
img_lena = io.imread("lena_std.tif")
hist_lena = hist_rgb(img_lena)
mat_hist_rgb(hist_lena)
赤成分が多めであることが分かります。
$\gamma=0.5$の場合
img_gamma = gammma(img_lena, r=0.5)
io.imsave("r05.png", img_gamma)
mat_hist_rgb(hist_rgb(img_gamma))
ヒストグラムを見ると元画像と比較して、だいぶ暗いことが分かります。
$\gamma=2.0$の場合
img_gamma = gammma(img_lena, r=2.0)
io.imsave("r05.png", img_gamma)
mat_hist_rgb(hist_rgb(img_gamma))
相当明るくなります。
特定の色だけ補正
ヒストグラムを見ると、lenaさんの画像は全体的に赤味がかかった画像であることが分かります。
そこで特定の色、今回の場合は赤色のチャンネルのみにガンマ補正をかけてみます。
# channelで変換するRGBを指定, R=0, G=1, B=2
channel = 0
#元の画像をコピー
img_gamma = np.copy(img_lena)
# ガンマ補正
img_gamma[:,:,channel] = gammma(img_lena[:,:,channel], r=0.3)
io.imsave("red.png", img_gamma)
mat_hist_rgb(hist_rgb(img_gamma))
$\gamma=0.3$なので、赤色の成分を弱めています。こちらのほうが自然な感じがします。
参考文献
ディジタル画像処理[改訂第二版], 公益財団法人 画像情報教育振興協会(2020)