LoginSignup
4
12

More than 1 year has passed since last update.

画像2値化の前処理にCLAHEを使う

Last updated at Posted at 2022-05-11

2値化は画像処理の基本だが・・・

オープンソース化が進み、高度な画像処理が誰でも簡単に試せるようになった昨今ですが、
2値化処理は画像処理の基本として必ずおさえておきたい技術です。

画像の2値化処理とは、所定の閾値をもって画像を白と黒の2色に変換する処理です

  • 2値化画像の入力を前提とする画像処理アルゴリズムが多くある
  • 2値化画像を題材とした加工事例が豊富にある
  • 通常画像の特定部位を2値化画像でマスキングすることで、領域を限定した加工もできる

思い通りの2値化画像を手にすれば、その後の画像加工のアイディアもグンと広がります。

しかし・・・ 「期待したような2値化が上手く出来ない!」
これもまたよくあります(苦笑)

例えば、こんな画像で考えてみましょう。
キャプチャ01.JPG
さて、このリング部分を2値化で捉えることを考えてみます。
画像2値化の閾値(thresh)を段階的に調整してみました。
キャプチャ02.JPG

しかし、どの閾値で2値化しても上手くリングを捉えることができなかったようです。

  • リングの左上部を残す閾値だとリング右下部が残らない
  • 逆に右下部を残そうとすると左上部が残らない

CLAHEによる明るさムラの調整

思惑どおりの2値化が出来なかったのは、画像内での明るさにムラがあり変化しているためです。

実践においては「あるある」ですね!

こんな時にはCLAHEを試してみましょう。
CLAHEは「Contrast Limited Adaptive Histogram Equalization」の略称で、
日本語にすると「コントラスト制限付き適用的ヒストグラム平均化」です。
CLAHE - Wikipedia
OpneCVチュートリアル:CLAHE (Contrast Limited Adaptive Histogram Equalization)

さあ、では早速試してみましょう。
左がCLAHE適用前、右が適用後です。
キャプチャ03.JPG
CLAHEは、一旦画像を細かい区画に分け、個別に明るさ調整した後に再合成するため、
加工結果は少しぎくしゃくとした状態にはなりますが、リングの部分はより鮮明になったように見えます。

2値化に再チャレンジ

さあ、CLAHE適用後の画像で改めて2値化をしてみましょう。
キャプチャ04.JPG

今度は閾値(thresh)が160~180 あたりで見事にリングの2値化に成功しました!

以上、画像に明るさムラがあり2値化が上手くいかなかったときのヒントとなれば幸いです。
最後までお読みいただき、ありがとうございました。

サンプルコード

最後に本記事のサンプルコードも載せておきます。

CLAHE_try.ipynb

%matplotlib inline
import math
import numpy as np
import matplotlib.pyplot as plt
import cv2

# 2値化閾値を変化させplot
def plot_threshold(img, threshStart, threshEnd, threshStep):
    imgs=[]
    for thresh in range(threshStart, threshEnd+1, threshStep):  
        _, dst = cv2.threshold(img, thresh, maxval=255, type=cv2.THRESH_BINARY)
        imgs.append(dst)

    col = 4
    row = (len(imgs)-1)//col + 1
    plt.figure(figsize=(col*3,row*3), facecolor='white')
    plt.subplots_adjust(wspace=0.0, hspace=0.2)

    for i, img in enumerate(imgs):
        plt.subplot(row, col, i+1)
        plt.imshow(img, cmap='gray')
        plt.title(f'thresh={threshStart+threshStep*i}')
        ax = plt.gca()
        ax.axes.xaxis.set_ticks([])
        ax.axes.yaxis.set_ticks([])

    plt.show()

# 検証用のoriginal画像の生成
im_org = np.zeros((256,256), dtype=np.uint8)
h, w = im_org.shape
im_org = cv2.circle(im_org, (w//2, h//2), w//3, 32, thickness=10)
for x in range(h):
    for y in range(w):
        add = int(math.sqrt(x**2+y**2)/1.5)
        im_org[y:y+1, x:x+1] += add 

# CLAHE適用
clahe = cv2.createCLAHE(clipLimit=8.0, tileGridSize=(8,8))
im_clahe = clahe.apply(im_org)   

# OriginalとCLAHE適用後の画像を表示
plt.figure(figsize=(15,18), facecolor='white')
plt.subplots_adjust(wspace=0.1, hspace=0.2)
plt.subplot(1, 2, 1)
plt.imshow(im_org, cmap='gray')
plt.title(f'Orignal')
plt.subplot(1, 2, 2)
plt.imshow(im_clahe, cmap='gray')
plt.title(f'After CLAHE')
plt.show()

# Original画像の2値化を試行
plot_threshold(im_org, 80, 190, 10)

# CLAHE適用後画像の2値化を試行
plot_threshold(im_clahe, 80, 190, 10)

動作確認環境:
python 3.6.9 + numpy 1.16.5 + opencv 3.4.4
python 3.9.7 + numpy 1.22.3 + opnecv 4.5.5

4
12
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
4
12