LoginSignup
46
70

More than 3 years have passed since last update.

画像の輝度を正規化

Last updated at Posted at 2020-03-10

実行環境

Ubuntu 18.04
Python 3.6.9
OpenCV 3.3.0

概要

大量の画像を処理する際、明るい写真や暗い写真が入り混じっており、
処理結果にも影響してしまうので、明るさを統一したい!

という思いから、
学習の前処理で画像の輝度を調整する機会があったため、手法をまとめておく。
入力するカラー画像を、一様な明るさに正規化することを目的とする。
やってみた手法は以下の3種類。

①ヒストグラム平坦化
②適応的ヒストグラム平坦化
③輝度値の平均と標準偏差を指定

検証用の写真はこちら。靄がかかったタージマハル
taj-mahal-1209004_1280.jpg
また、この画像の輝度ヒストグラムは次のように分布している。
100〜230あたりにかけて画素値が集中していることがわかる。
original.png

使用するライブラリ

import cv2
import numpy as np
from matplotlib import pyplot as plt

前処理

輝度値を扱うため、まずグレースケールの画像を取得する。
OpenCV の imread で引数を指定すると一発でグレースケール画像を取得できるが、
今回はカラー画像の輝度のみ変更する趣旨なので、色情報も取得しておく。
そこで、画像を読み込み、RGB(OpenCV だと BGR)から HSV への変換を行う。

HSVは画像を
・Hue(色相)
・Saturation(彩度)
・Value(輝度)
で表しており、この中で輝度の Value を用いる。

Value を取得するコードは以下の通り。

img=cv2.imread(str(file)) # 画像読み込み
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # hsv票色系に変換
h,s,v = cv2.split(hsv) # 各成分に分割

h,s,v の中で v が Value であり、これをグレースケール画像として扱う。

なお、画像のヒストグラムは matplotlib の関数で簡単に表示できる。

plt.hist(v.ravel(),256,[0,256]);plt.show()

後処理

先に後処理について記載しておくと、
①〜③の結果を格納する変数 result は輝度値を表すグレースケール画像のため、
最後に前処理と逆の処理を行い、カラー画像に戻している。

hsv = cv2.merge((h,s,result))
rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
cv2.imwrite("result.jpg", rgb) # 出力する場合

①ヒストグラム平坦化

1つ目の手法は、画像のヒストグラム(輝度分布)を平坦化するというもの。
ヒストグラムの平坦化は次のようなイメージ。

Histogrammeinebnung.png
「平坦化」という言葉から、画像が滑らかになる印象が持たれるが、
実際は真っ黒な画素から真っ白な画素まで満遍なく出現するよう画素の値を調整するため、
コントラストが強調され、はっきりした画像になる。

この手法は OpenCV の関数で確認できる。
コードは以下の通り。

result = cv2.equalizeHist(v)

結果の画像とヒストグラムがこちら。
result.jpg

equal.png

ヒストグラムを見ると、250付近の画素が離散的になっている。
これは、結果画像右上の光のグラデーション部分に表れており、コントラストが強調された影響でトーンジャンプが発生している。
jagi.jpg
元画像の輝度値のヒストグラム分布が一様ではないため発生するらしい。
もう少し丁寧にヒストグラムを平坦化させる手法が、次の適応的ヒストグラム平坦化。

②適応的ヒストグラム平坦化

2つ目の手法は、①のヒストグラム平坦化を小領域ごとに行うというもの。
詳しい仕組みやパラメータはリファレンスを参照。
これにより、①より高精度な平坦化が可能だが、パラメータを間違えると強調されすぎてしまうため注意。

適応的ヒストグラム平坦化の実装は以下の通り。

clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(3, 3))
result = clahe.apply(v)

clipLimit はコントラストを制限するためのパラメータ。
tileGridSize は小領域のサイズで、今回は3x3の領域で行っている。

結果がこちら。
adaptive.jpg
adaptive.png
ヒストグラムから、①より連続的に画素値が分布していることがわかる。
①で発生していたトーンジャンプが緩和され、バランスの取れたヒストグラムになった。

③輝度の平均と標準偏差を指定

3つ目はこちらのサイトを参考にした。
画像の輝度の平均値と標準偏差を取得し、それらを任意の値に変更するという手法。

v = (v-np.mean(v)) / np.std(v) * s + m
result = np.array(v, dtype=np.uint8) # 配列のdtypeをunit8に戻す
  • np.mean(v) :輝度の平均値
  • np.std(v) :輝度の標準偏差

まず、元画像から輝度の平均値を引き標準偏差で割ることで、平均輝度0、標準偏差1の画像を生成している。
そこから任意の値 s をかけることで標準偏差を設定し、さらに任意の値 m を足すことで輝度平均を設定する。
今回は s=32 、m=128 で設定している。

結果はこちら。
mean32.jpg
mean32.png

ヒストグラムより、輝度平均128を中心に画素が集約されていることが確認できる。
今回はカラー画像に戻す際に整数型に変換するので、
計算結果で輝度が0~255の範囲を外れる場合は結果がおかしくなるため、調整が必要。

おわりに

最後に、各結果をまとめて掲載しておく。
ヒストグラム平坦化後の条件が異なるので性能の比較実験ということではないが、参考まで。
左から、
元画像 ー ①結果 ー ②結果 ー ③結果
marge1.jpg
marge2.jpg
各手法で、様々な環境下で撮影された画像を一定の基準に合わせることができる。

今回は輝度のみ正規化を行ったが、コントラストや彩度も同様に正規化することで、
より一様な画像を作成できるだろう。

参考文献

http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_histograms/py_histogram_equalization/py_histogram_equalization.html
https://cvtech.cc/std/

46
70
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
46
70