【Python・OpenCV】二値化による閾値処理を行うには(cv2.threshold)

※当サイトではアフィリエイト広告を利用しています

Python プログラミング 画像処理

【Python・OpenCV】二値化による閾値処理を行うには(cv2.threshold)

2023-12-05

はじめに

画像処理において、二値化は画像を白黒の2つの色に変換するプロセスで、画像から特定のオブジェクトを抽出する、または画像処理の前処理として使用されることがあります。
本記事ではサンプルコードを含めながら、大津の二値化と単純な閾値処理の二値化について説明します。

二値化とは

画像処理の二値化は、画像の各ピクセルの明るさや色情報を考慮し、それに基づいてピクセルの値を2つのカテゴリ(通常は白と黒)に分類する処理です。
具体的には、ある閾値よりも明るい場合と暗い場合で分け、それに基づいて新しい値を割り当てます。

一般的に、以下の手順で二値化が行われます。

  1. グレースケール変換:
    カラー画像の場合、まずグレースケールに変換します。これにより、各ピクセルは単一の明るさの値を持つようになります。
  2. 閾値の設定:
    二値化では、ある特定の明るさの閾値を決定します。この閾値を超えるかどうかによってピクセルを分類します。
  3. ピクセルの分類:
    各ピクセルが閾値を超えるかどうかに応じて、新しい値を割り当てます。通常、閾値を超えた場合は白、そうでない場合は黒になります。

例えば、ピクセルの値が閾値より大きい場合は255(白)、閾値以下の場合は0(黒)といった具体的な割り当てが一般的です。
この結果、画像が白と黒の二値の形に変換され、対象物体や特定の特徴がハッキリと際立つようになります。

cv2.threshold関数

OpenCVで二値化を行うにはcv2.threshold関数を使用します。

thresholded = cv2.function(入力画像, thresh, maxval, type)

引数

名称説明
入力画像(必須)処理対象の画像(通常はグレースケール画像)。カラー画像を処理する場合は、事前にグレースケール変換が必要です。
thresh(必須)閾値。この値を基準にしてピクセルの値が変更されます。例えば、閾値が 127 の場合、ピクセルの値が 127 より大きいか小さいかで二分されます。
maxval(必須)閾値を超えた場合に設定される値。通常は白(255)に設定します。
type(必須)閾値処理のタイプ。

type(閾値処理のタイプ)は以下の値が指定できます。

type説明
cv2.THRESH_BINARY画素の輝度値が閾値を超える場合は maxval、そうでない場合は 0 に設定。
cv2.THRESH_BINARY_INV画素の輝度値が閾値を超える場合は 0、そうでない場合は maxval に設定。
cv2.THRESH_TRUNC画素の輝度値が閾値を超える場合は閾値に、そうでない場合はそのままの値に設定。
cv2.THRESH_TOZERO画素の輝度値が閾値を超える場合はそのままの値に、そうでない場合は 0 に設定。
cv2.THRESH_TOZERO_INV画素の輝度値が閾値を超える場合は 0 に、そうでない場合はそのままの値に設定。
cv2.THRESH_OTSU大津の二値化。画像のヒストグラム解析を用いて自動的に閾値を決定する手法です。
cv2.THRESH_TRIANGLEトライアングル アルゴリズムにより自動的に閾値を決定する手法です。
type(閾値処理のタイプ)

戻り値

cv2.threshold 関数の戻り値は2つの要素からなるタプルです。

retval:の値は、実際に適用された閾値です。処理中にcv2.THRESH_OTSUを使って自動的に閾値が計算された場合、この値に計算された閾値が格納されます。通常の手動の場合は、引数で指定した閾値そのものが retval として返ります。


thresh:この変数には、閾値処理が適用された結果の画像が格納されます。ピクセルの値が閾値を超えると、指定した maxval に設定され、それ以下の場合は 0 に設定された画像です。

(広告) OpenCV関連書籍をAmazonで探す

使い方

サンプルコードを紹介します。
入力画像はカラー画像ではなくグレースケール画像を対象としています。
入力画像として、この画像を使用しています。cv2.imread関数でグレースケールに変換しました。

全ての閾値処理のタイプの結果を表示するサンプルコードとなっています。

ポイント

閾値タイプのcv2.THRESH_OTSUcv2.THRESH_TRIANGLEでは、引数のthreshにどの様な値を設定しても無視されます。

import cv2
import matplotlib.pyplot as plt

# 画像をグレースケールで読み込む
image = cv2.imread("image.jpg", cv2.IMREAD_GRAYSCALE)


# 閾値処理を行う
# Type: cv2.THRESH_BINARY, 閾値の指定: 127
retval, thresh_binary = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)

# Type: cv2.THRESH_BINARY_INV, 閾値の指定: 127
retval, thresh_binary_inv = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY_INV)

# Type: cv2.THRESH_TRUNC, 閾値の指定: 127
retval, thresh_trunc = cv2.threshold(image, 127, 255, cv2.THRESH_TRUNC)

# Type: cv2.THRESH_TOZERO, 閾値の指定: 127
retval, thresh_tozero = cv2.threshold(image,127, 255, cv2.THRESH_TOZERO)

# Type: cv2.THRESH_TOZERO_INV, 閾値の指定: 127
retval, thresh_tozero_inv = cv2.threshold(image, 127, 255, cv2.THRESH_TOZERO_INV)

# Type: cv2.THRESH_OTSU, 閾値の指定は不要ですが任意の値として0を設定
retval, thresh_otsu = cv2.threshold(image, 0, 255, cv2.THRESH_OTSU)

# Type: cv2.THRESH_TRIANGLE, 閾値の指定は不要ですが任意の値として0を設定
retval, thresh_triangle = cv2.threshold(image, 0, 255, cv2.THRESH_TRIANGLE)


# 結果を表示
plt.rcParams["figure.figsize"] = [9,9]                                  # ウィンドウサイズを設定
title = "cv2.threshold: codevace.com"
plt.figure(title)                                                       # ウィンドウタイトルを設定
plt.subplots_adjust(left=0.05, right=0.95, bottom=0.03, top=0.95)       # 余白を設定
plt.subplot(421)                                                        # 4行2列の1番目の領域にプロットを設定
plt.imshow(image, cmap='gray')                                          # 入力画像をグレースケールで表示
plt.title('Original Image')                                             # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す
plt.subplot(422)                                                        # 4行2列の2番目の領域にプロットを設定
plt.imshow(thresh_binary, cmap='gray')                                  # cv2.THRESH_BINARY, thresh=127の結果
plt.title('cv2.THRESH_BINARY, thresh=127')                              # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す
plt.subplot(423)                                                        # 4行2列の2番目の領域にプロットを設定
plt.imshow(thresh_binary_inv, cmap='gray')                              # cv2.THRESH_BINARY_INV, thresh=127の結果
plt.title('cv2.THRESH_BINARY_INV, thresh=127')                          # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す
plt.subplot(424)                                                        # 4行2列の4番目の領域にプロットを設定
plt.imshow(thresh_trunc, cmap='gray')                                   # cv2.THRESH_TRUNC, thresh=127の結果
plt.title('cv2.THRESH_TRUNC, thresh=127')                               # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す
plt.subplot(425)                                                        # 3行2列の5番目の領域にプロットを設定
plt.imshow(thresh_tozero, cmap='gray')                                  # cv2.THRESH_TOZERO, thresh=127の結果
plt.title('cv2.THRESH_TOZERO, thresh=127')                              # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す
plt.subplot(426)                                                        # 4行2列の6番目の領域にプロットを設定
plt.imshow(thresh_tozero_inv, cmap='gray')                              # cv2.THRESH_TOZERO_INV, thresh=127の結果
plt.title('cv2.THRESH_TOZERO_INV, thresh=127')                          # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す
plt.subplot(427)                                                        # 4行2列の6番目の領域にプロットを設定
plt.imshow(thresh_otsu, cmap='gray')                                    # cv2.THRESH_OTSUの結果
plt.title('cv2.THRESH_OTSU')                                            # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す
plt.subplot(428)                                                        # 4行2列の6番目の領域にプロットを設定
plt.imshow(thresh_triangle, cmap='gray')                                # cv2.THRESH_TRIANGLEの結果
plt.title('cv2.THRESH_TRIANGLE')                                        # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す
plt.show()

サンプルコードの結果が下の図です。

cv2.THRESH_OTSUcv2.THRESH_TRIANGLEは閾値をアルゴリズムが決定してくれるので、使いやすい場合があります。
cv2.THRESH_OTSUは対象物体と背景のコントラストがはっきりしている場合に効果的ですが、そうでない場合は手動の閾値指定が適しています。
また、cv2.THRESH_TRIANGLEcv2.THRESH_OTSUと比較すると、もう少し対象物体と背景切り分けを正確に行なうことができる様ですが、対象物の細かな形状は正しく検出できないことがある様です。

おわりに

OpenCVのcv2.threshold関数を使用して画像の二値化を行う基本的な手順を解説しました。
閾値処理のタイプ違いについて基本的な動作は理解頂けたかと思います。

また、他の二値化を行う関数としてcv2.adaptiveThreshold関数があり、この記事で解説しています。

ご質問や取り上げて欲しい内容などがありましたら、コメントをお願いします。
最後までご覧いただきありがとうございました。

(広告) OpenCV関連書籍をAmazonで探す

参考リンク

-Python, プログラミング, 画像処理
-