OpenCV のヒストグラム平坦化でコントラストを強調する

画像のコントラストを上げるのに、OpenCV の cv2.equalizeHist や、cv2.createCLAHE といったヒストグラム平坦化機能が活用できる。これらを試してみた結果を投稿する。

元画像

元画像は野鳥観察壁。壁を主役にするには、少し暗い。この画像を加工し、コントラストを強調する。

野鳥観察壁:小窓からバードウォッチングができる

cv2.equalizeHist で各色を平坦化

明るさのヒストグラムを平坦にするために、cv2.equalizeHist を使ってみた。cv2.equalizeHist は単色でしか扱えないため、cv2.split で b, g, r の3色にし、それぞれヒストグラム平坦化をした。

cv2.equalizeHist で各色をヒストグラム平坦化
    # 各色で平坦化                                                                                                                          
    b1,g1,r1 = cv2.split(img)
    b2 = cv2.equalizeHist(b1)
    g2 = cv2.equalizeHist(g1)
    r2 = cv2.equalizeHist(r1)
    eqh_rgb = cv2.merge((b2,g2,r2))

壁の様子は分かるようになったが、もともと少なかった赤成分が不自然に強調され、元の画像から印象が変化した。

cv2.equalizeHist で HSV 空間の V を平坦化

色合いを保存しつつ平坦化するために、色空間を BGR から HSV に変換した後に cv2.equalizeHist を適用する。

cv2.equalizeHist で HSV 空間の V を平坦化
    # HSVのvだけ編集                                                                                                                        
    h1,s1,v1 = cv2.split(cv2.cvtColor(img,cv2.COLOR_BGR2HSV))  # 色空間をBGRからHSVに変換                                                   
    v2 = cv2.equalizeHist(v1)
    eqh_hsv = cv2.cvtColor(cv2.merge((h1,s1,v2)), cv2.COLOR_HSV2BGR)

出力したかった画像に近づいた。ただ、同じ色の面積が大きいと、少しザラザラした印象になる。

cv2.createCLAHE による平坦化

cv2.createCLAHE でも調整ができる。cv2.createCLAHE は部分的にヒストグラム平坦化を行いコントラスト強調する。equalizeHist と違い、2つの引数(lipLimit と tileGridSize) を持てるため、引数を変更することで画像の調整が可能になる。clipLimit を大きくすればコントラストが強くなり、荒くなる。tileGridSize は大きいと大局的に平坦化、小さいと部分的に平坦化する。

cv2.createCLAHE も単色のみを扱える。cv2.split で b, g, r の3色にしてから、平坦化を行う。

cv2.createCLAHE による平坦化 パラメータは clipLimit = 2. と tileGridSize = (4, 4) の設定
# 部分的にヒストグラム平坦化でコントラスト強調                                                                                              
def clc(img,cl,gsize):
    b1,g1,r1 = cv2.split(img)
    clahe = cv2.createCLAHE(clipLimit=cl, tileGridSize=(gsize,gsize))
    b2 = clahe.apply(b1)
    g2 = clahe.apply(g1)
    r2 = clahe.apply(r1)
    return cv2.merge((b2,g2,r2))

clc_img = clc(img,2.,4)

clipLimit が大きいのか、やや白っぽい画像になった。

全部混ぜて画像を作る

元画像を含め、4枚の画像の平均をとって、無難な画像に仕上げる。画像の扱いに限らず一般的に、複数のアルゴリズムを混ぜれば大抵のものに対応できるようになることが多い。

4枚の画像の平均
# 値を0-255にclipして、typeをuint8にする                                                                                                    
def ct(img):
    return np.clip(img,0,255).astype(np.uint8)

# 全部混ぜる                                                                                                                                
ave_img = ct((np.float32(img) \
            + np.float32(eqh_rgb) \
            + np.float32(eqh_hsv) \
            + np.float32(clc_img))/4.)

コード

下記は今回のコードの全文。上記で紹介した、各画像を出力する。

import cv2
import sys
import numpy as np

# ヒストグラム平坦化でコントラスト強調                                                                                                      
def eqh(img):
    # 各色で平坦化                                                                                                                          
    b1,g1,r1 = cv2.split(img)
    b2 = cv2.equalizeHist(b1)
    g2 = cv2.equalizeHist(g1)
    r2 = cv2.equalizeHist(r1)
    eqh_rgb = cv2.merge((b2,g2,r2))

    # HSVのvだけ編集                                                                                                                        
    h1,s1,v1 = cv2.split(cv2.cvtColor(img,cv2.COLOR_BGR2HSV))  # 色空間をBGRからHSVに変換                                                   
    v2 = cv2.equalizeHist(v1)
    eqh_hsv = cv2.cvtColor(cv2.merge((h1,s1,v2)), cv2.COLOR_HSV2BGR)

    return eqh_rgb, eqh_hsv

# 部分的にヒストグラム平坦化でコントラスト強調                                                                                              
def clc(img,cl,gsize):
    b1,g1,r1 = cv2.split(img)
    clahe = cv2.createCLAHE(clipLimit=cl, tileGridSize=(gsize,gsize))
    b2 = clahe.apply(b1)
    g2 = clahe.apply(g1)
    r2 = clahe.apply(r1)
    return cv2.merge((b2,g2,r2))

# 値を0-255にclipして、typeをuint8にする                                                                                                    
def ct(img):
    return np.clip(img,0,255).astype(np.uint8)

file_name = sys.argv[1]
img = cv2.imread(file_name)

# コントラスト強調画像を作成                                                                                                                
eqh_rgb, eqh_hsv = eqh(img)
clc_img = clc(img,2.,4)

# 全部混ぜる                                                                                                                                
ave_img = ct((np.float32(img) \
            + np.float32(eqh_rgb) \
            + np.float32(eqh_hsv) \
            + np.float32(clc_img))/4.)

# 保存する                                                                                                                                  
cv2.imwrite(file_name.replace(".",".out_eqh_rgb."),eqh_rgb)
cv2.imwrite(file_name.replace(".",".out_eqh_hsv."),eqh_hsv)
cv2.imwrite(file_name.replace(".",".out_clc."),clc_img)
cv2.imwrite(file_name.replace(".",".out_all."),ave_img)

コメント

タイトルとURLをコピーしました