iMind Developers Blog

iMind開発者ブログ

Pythonでヒストグラム平坦化とガンマ補正

概要

暗いところで撮った写真をさらっと明るくしたい。

バージョン情報

  • Python 3.6.5
  • opencv-python==3.4.2.17

サンプル画像

暗いところで撮影したかわいいワンコの画像があります。

f:id:imind:20190212200051p:plain

暗い中でもそのかわいさは隠しきれませんが、よりかわいさが見えやすくなるように補正をかけていきます。

ヒストグラム平坦化

ヒストグラム平坦化は名前の通り、画像のヒストグラムを平坦にします。

平坦化する前の左の画像のRGBそれぞれのヒストグラムを確認します。

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

img = cv2.imread('data/dark_image/001.jpg')
f, ax_list = plt.subplots(3, 1, figsize=(10, 5))
for i in range(3):
    ax = ax_list[i]
    ax.hist(img.flatten(), bins=50)

f:id:imind:20190212200501p:plain

X軸がRGB各色の0〜255の値(0に近いほど黒、255に近いほど白)、Y軸はそのピクセル数です。

見ての通り、0〜60くらいの間に値が偏っています。これをうまいこと平坦にすると暗い画像が自然と明るくなります。

ヒストグラム平坦化はOpenCVのequalizeHistで実行できます。

まずは1つ目の画像に対してヒストグラム平坦化を実行してヒストグラムが同変化したかを確認します。

img = cv2.imread('data/dark_image/001.jpg')
img_hist_eq = img.copy()

# RGBそれぞれを平坦化
for i in range(3):
    img_hist_eq[:, :, i] = cv2.equalizeHist(img_hist_eq[:, :, i])

# 平坦化後のヒストグラム表示
f, ax_list = plt.subplots(3, 1, figsize=(10, 6))
for i in range(3):
    ax = ax_list[i]
    ax.hist(img_hist_eq[:, :, i].flatten(), bins=50)

f:id:imind:20190212202901p:plain

加工前は0〜60に偏っていたのが、0〜255に平坦に値が散っています。

結果を画像として表示します。

f, ax_list = plt.subplots(1, 3, figsize=(15, 6))
for i in range(1, 4):
    img = cv2.imread('data/dark_image/00{}.jpg'.format(i))
    for j in range(3):
        img[:, :, j] = cv2.equalizeHist(img[:, :, j])
    ax_list[i - 1].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

f:id:imind:20190212203512p:plain

左端の画像は元からそれなりに明るさがあったので結果も良好ですが、右端は元画像が暗すぎる為か発光してしまっています。加工元の画像にはある程度の光量は必要になります。

ガンマ補正

ガンマ補正は各色の分布をカーブ上に補正をかける手法。

下記はGimpの色 → レベルでガンマを入力してトーンカーブを見た例。

f:id:imind:20190212204802p:plain

1つ目の画像に対してガンマ補正を実行し、ヒストグラムの変化を見てみます。

変数ガンマには2を入れています。数値が大きいほど画像は明るくなります。

img = cv2.imread('data/dark_image/001.jpg')

# ガンマ補正
gamma = 2
table = np.array([((i / 255.0) ** (1 / gamma)) * 255 for i in np.arange(0, 256)]).astype("uint8")
img_gamma = cv2.LUT(img, table)

f, ax_list = plt.subplots(3, 1, figsize=(10, 6))
for i in range(3):
    ax = ax_list[i]
    ax.hist(img_gamma[:, :, i].flatten(), bins=50)

f:id:imind:20190212210205p:plain

元画像は50付近に山があったのが110〜120付近が頂点になっていますね。ヒストグラム平坦化の時のように0〜255に平坦な分布になるのではなく、元の分布をなだからに底上げしたような結果になっています。

各画像に補正をかけて結果画像を表示してみます。

img = cv2.imread('data/dark_image/001.jpg')

# ガンマ補正
gamma = 2
table = np.array([((i / 255.0) ** (1 / gamma)) * 255 for i in np.arange(0, 256)]).astype("uint8")
img_gamma = cv2.LUT(img, table)

f, ax_list = plt.subplots(3, 1, figsize=(10, 6))
for i in range(3):
    ax = ax_list[i]
    ax.hist(img_gamma[:, :, i].flatten(), bins=50)

f:id:imind:20190212210444p:plain

平坦化のように鮮やかな色調にはなりませんが、極端に暗い画像でも発光するような現象は起きていません。

コントラスト補正

ガンマ補正のみでは薄味な画像になってしまうことがあるので、コントラストにも補正を入れてみます。

コントラストの調整は下記のサイトを参考にしました。

http://gori-naru.blogspot.com/2012/11/blog-post_8647.html

# ガンマ補正用のテーブル
gamma = 2
g_table = np.array([((i / 255.0) ** (1 / gamma)) * 255 for i in np.arange(0, 256)]).astype("uint8")

# コントラスト補正用のテーブル
a = 10
c_table = np.array([255.0 / (1 + np.exp(-a * (i - 128) / 255)) for i in np.arange(0, 256)]).astype("uint8")

f, ax_list = plt.subplots(1, 3, figsize=(15, 6))
for i in range(1, 4):
    img = cv2.imread('data/dark_image/00{}.jpg'.format(i))
    # ガンマ補正
    img_gamma = cv2.LUT(img, g_table)
    # コントラスト補正 
    img_gamma = cv2.LUT(img_gamma, c_table)
    ax_list[i - 1].imshow(cv2.cvtColor(img_gamma, cv2.COLOR_BGR2RGB))

f:id:imind:20190212211454p:plain

ちょっとくっきりしました。

でも真ん中の写真がちょっと暗い。

てきとーなガンマ補正の自動調整をするロジックを書いて再調整してみます。

# コントラスト補正用のテーブル
a = 10
c_table = np.array([255.0 / (1 + np.exp(-a * (i - 128) / 255)) for i in np.arange(0, 256)]).astype("uint8")

f, ax_list = plt.subplots(1, 3, figsize=(15, 6))
for i in range(1, 4):
    img = cv2.imread('data/dark_image/00{}.jpg'.format(i))
    # ガンマ補正用のテーブル(画像のmeanに応じて変動)
    gamma = 1 / np.sqrt(img.mean()) * 13
    g_table = np.array([((i / 255.0) ** (1 / gamma)) * 255 for i in np.arange(0, 256)]).astype("uint8")
    # ガンマ補正
    img_gamma = cv2.LUT(img, g_table)
    # コントラスト補正 
    img_gamma = cv2.LUT(img_gamma, c_table)
    ax_list[i - 1].imshow(cv2.cvtColor(img_gamma, cv2.COLOR_BGR2RGB))

f:id:imind:20190212212307p:plain

ほどよい感じになりました。

改定履歴

Author: Masato Watanabe, Date: 2019-02-13, 記事投稿