ラベリングとは、二値化された画像において、画素がつながっている領域に対して同じ番号(ラベル)を与える処理のことを言います。
このつながっている領域のことをブロブ(blob)といいますが、このブロブに対して、面積や幅、高さなどを求め、キズや打痕、汚れなどの欠陥検査を行うための前処理として、ラベリング処理が用いられることが多いです。
↓ラベリング
詳細はこちら↓を参照ください。
OpenCVでラベリング処理を行うには、connectedComponents()関数を用います。
関連して、connectedComponentsWithStats()、connectedComponentsWithStatsWithAlgorithm()という関数もあります。
connectedComponents()関数は、二値化画像に対して、ラベリング画像のみを出力します。
connectedComponentsWithStats()関数は、ラベリング画像に追加して、さらにブロブの領域(位置、幅、高さ)と面積、重心の情報を出力します。
connectedComponentsWithStatsWithAlgorithm()関数は、さらに、ラベリング処理時の4連結、8連結の指定や出力するラベリング画像のデータ型の指定、ラベリング処理アルゴリズムの指定が可能になっています。
処理アルゴリズムについては、詳細は分からなかったのですが、こちら↓を参照くだだい。
https://docs.opencv.org/4.6.0/d3/dc0/group__imgproc__shape.html#ga5ed7784614678adccb699c70fb841075
connectedComponents()関数
connectedComponents( image[, labels[, connectivity[, ltype]]] ) -> retval, labels
引数 | 説明 |
image | ラベリング処理を行う入力画像 8bit1chである必要があります。 |
labels | ラベリング結果を格納する画像データ 戻り値で戻すこともできるので、指定しないか、Noneを指定でも大丈夫です。 |
connectivity | 8連結の場合は 8、4連結の場合は 4 を指定します。 |
ltype | 出力されるラベリング画像の型を指定します。 cv2.CV_16U もしくは cv2.CV_32S(初期値) |
(戻り値)retval | ラベルの個数が返されます。 ただし、背景も1つとしてカウントされます。 |
(戻り値)labels | 出力されるラベリング画像 |
connectedComponentsWithStats()関数
connectedComponentsWithStats( image[, labels[, stats[, centroids[, connectivity[, ltype]]]]] ) -> retval, labels, stats, centroids
引数 | 説明 |
image | ラベリング処理を行う入力画像 8bit1chである必要があります。 |
labels | ラベリング結果を格納する画像データ 戻り値で戻すこともできるので、指定しないか、Noneを指定でも大丈夫です。 |
connectivity | 8連結の場合は 8、4連結の場合は 4 を指定します。 |
ltype | 出力されるラベリング画像の型を指定します。 cv2.CV_16U もしくは cv2.CV_32S(初期値) |
(戻り値)retval | ラベルの個数が返されます。 ただし、背景も1つとしてカウントされます。 |
(戻り値)labels | 出力されるラベリング画像 |
(戻り値)stats | 各ラベルごとの領域の情報が格納されます。 [領域の左上のx座標, 領域の左上のy座標, 領域の幅, 領域の高さ, 面積] 面積は領域の画素数です。 |
(戻り値)centroids | 各ラベルごとの重心の情報が格納されます。 |
※labels, stats, centroidsの最初のデータは背景の情報となります。
connectedComponentsWithStatsWithAlgorithm()関数
connectedComponentsWithStatsWithAlgorithm( image, connectivity, ltype, ccltype[, labels[, stats[, centroids]]] ) -> retval, labels, stats, centroids
引数 | 説明 |
image | ラベリング処理を行う入力画像 8bit1chである必要があります。 |
labels | ラベリング結果を格納する画像データ 戻り値で戻すこともできるので、指定しないか、Noneを指定でも大丈夫です。 |
connectivity | 8連結の場合は 8、4連結の場合は 4 を指定します。 |
ltype | 出力されるラベリング画像の型を指定します。 cv2.CV_16U もしくは cv2.CV_32S(初期値) |
ccltype | ラベリング処理アルゴリズムを指定します。 cv2.CCL_DEFAULT, cv2.CCL_WU, cv2.CCL_GRANA, cv2.CCL_BOLELLI, cv2.CCL_SAUF, cv2.CCL_BBDT, cv2.CCL_SPAGHETTI (参考) https://docs.opencv.org/4.6.0/d3/dc0/group__imgproc__shape.html#ga5ed7784614678adccb699c70fb841075 |
(戻り値)retval | ラベルの個数が返されます。 ただし、背景も1つとしてカウントされます。 |
(戻り値)labels | 出力されるラベリング画像 |
(戻り値)stats | 各ラベルごとの領域の情報が格納されます。 [領域の左上のx座標, 領域の左上のy座標, 領域の幅, 領域の高さ, 面積] 面積は領域の画素数です。 |
(戻り値)centroids | 各ラベルごとの重心の情報が格納されます。 |
※labels, stats, centroidsの最初のデータは背景の情報となります。
connectedComponentsWithStats()関数は、おそらく下記のようにするのと同じです。(アルゴリズムの指定がちょっと怪しい。。)
retval, labels, stats, centroids = cv2.connectedComponentsWithStatsWithAlgorithm(img, 8, cv2.CV_32S, cv2.CCL_DEFAULT)
ラベリング処理例
最初にも触れましたが、ラベリングは画像中のキズや打痕、汚れなどの検出に使われる場合が多いです。
下図のように画像中にキズ、打痕、ホコリのようなものがあるときに、黒い部分の面積が大きい部分を検出する例のサンプルプログラムを作成しました。
import cv2
import numpy as np
# 8ビット1チャンネルのグレースケールとして画像を読み込む
img = cv2.imread("image.jpg", cv2.IMREAD_GRAYSCALE)
# 画像表示用に入力画像をカラーデータに変換する
img_disp = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
# 二値化(二値化して白黒反転)
retval, img = cv2.threshold(img, 200, 255, cv2.THRESH_BINARY_INV)
# クロージング処理(細切れ状態を防ぐため)
kernel = np.ones((3, 3), np.uint8)
img = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel, iterations=15)
# ラベリング
retval, labels, stats, centroids = cv2.connectedComponentsWithStats(img)
# 結果表示
for i in range(1, retval):
x, y, width, height, area = stats[i] # x座標, y座標, 幅, 高さ, 面積
if area > 10: # 面積が10画素以上の部分
cv2.rectangle(img_disp,(x,y),(x+width,y+height),(0,0,255),2)
cv2.putText(img_disp, f"[{i}]:{area}", (x, y-10), cv2.FONT_HERSHEY_PLAIN, 1, (255, 0, 0), 1, cv2.LINE_AA)
cv2.imshow("image", img_disp)
cv2.waitKey()
実行結果
connectedComponentsWithStatsとfindContoursの違い
ブロブの幅や高さ、面積などを解析することをブロブ解析と言いますが、connectedComponentsWithStats()関数で取得できる情報は、ブロブの領域(傾いていない矩形領域)、面積、重心だけですが、輪郭処理ベースのfindContours()関数では、ブロブの傾いた領域や外接円、楕円近似、直線近似、周囲長、凸法など出来る事が多くあるので、必要に応じて使い分けるといいと思います。
ただし、connectedComponentsWithStats()関数はブロブの画素に関する情報を取得しますが、findContours()関数は、あくまでの輪郭の情報を取得するので注意してください。
例えば、下図のような画像のとき
connectedComponentsWithStats()関数では面積(画素数)は8となりますが、findContours()関数では面積(輪郭の内側の面積、上図の赤線の四角の面積)は4となり、さらに輪郭の内側に白では無い画素があっても考慮されません。