2値化は画像処理の基本だが・・・
オープンソース化が進み、高度な画像処理が誰でも簡単に試せるようになった昨今ですが、
2値化処理は画像処理の基本として必ずおさえておきたい技術です。
画像の2値化処理とは、所定の閾値をもって画像を白と黒の2色に変換する処理です
- 2値化画像の入力を前提とする画像処理アルゴリズムが多くある
- 2値化画像を題材とした加工事例が豊富にある
- 通常画像の特定部位を2値化画像でマスキングすることで、領域を限定した加工もできる
思い通りの2値化画像を手にすれば、その後の画像加工のアイディアもグンと広がります。
しかし・・・ 「期待したような2値化が上手く出来ない!」
これもまたよくあります(苦笑)
例えば、こんな画像で考えてみましょう。
さて、このリング部分を2値化で捉えることを考えてみます。
画像2値化の閾値(thresh)を段階的に調整してみました。
しかし、どの閾値で2値化しても上手くリングを捉えることができなかったようです。
- リングの左上部を残す閾値だとリング右下部が残らない
- 逆に右下部を残そうとすると左上部が残らない
CLAHEによる明るさムラの調整
思惑どおりの2値化が出来なかったのは、画像内での明るさにムラがあり変化しているためです。
実践においては「あるある」ですね!
こんな時にはCLAHEを試してみましょう。
CLAHEは「Contrast Limited Adaptive Histogram Equalization」の略称で、
日本語にすると「コントラスト制限付き適用的ヒストグラム平均化」です。
CLAHE - Wikipedia
OpneCVチュートリアル:CLAHE (Contrast Limited Adaptive Histogram Equalization)
さあ、では早速試してみましょう。
左がCLAHE適用前、右が適用後です。
CLAHEは、一旦画像を細かい区画に分け、個別に明るさ調整した後に再合成するため、
加工結果は少しぎくしゃくとした状態にはなりますが、リングの部分はより鮮明になったように見えます。
2値化に再チャレンジ
さあ、CLAHE適用後の画像で改めて2値化をしてみましょう。
今度は閾値(thresh)が160~180 あたりで見事にリングの2値化に成功しました!
以上、画像に明るさムラがあり2値化が上手くいかなかったときのヒントとなれば幸いです。
最後までお読みいただき、ありがとうございました。
サンプルコード
最後に本記事のサンプルコードも載せておきます。
%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