4_15_画像セグメンテーションと分水界アルゴリズム-OpenCV中国の公式ドキュメント
流域アルゴリズムを使用してマーカーベースの画像セグメンテーションを実装する方法を学習します-次のように表示されます:cv.watershed()
要約
- グレースケール画像は、高強度が山の山を表し、低強度が谷を表す地形表面と考えることができます。
- OpenCVは、マーカーベースの流域アルゴリズムを実装します。このアルゴリズムでは、マージする谷のポイントとマージしない谷のポイントを指定できます。
- 距離変換と流域を使用して、接触しているオブジェクトをセグメント化します
ステップ
- コインのおおよその見積もりを見つけることから始めます。したがって、大津の二値化を使用できます。
- コインとして識別できる領域を抽出します。侵食は境界ピクセルを削除します。ですから、いくら残っていてもコインだと確信できます。これは、オブジェクトが互いに接触していない場合に機能します。ただし、それらは互いに接触しているため、別の適切なオプションは、距離変換を見つけて適切なしきい値を適用することです。
- コインではないと確信できるエリアを見つけてください。この目的のために、結果を拡張します。Inflateは、オブジェクトの境界を背景に追加します。このように、境界領域が削除されるため、結果の背景のすべての領域が実際に背景であると確信できます。下の画像を参照してください。
- 残りのエリアは、コインなのか背景なのか、私たちが知らないエリアです。流域アルゴリズムはそれを見つける必要があります。これらの領域は通常、前景と背景が出会うコインの境界の近くにあります(または2つの異なるコインですら)。それを境界と呼びます。面積から面積を
sure_bg
引くことで求められます。sure_fg
仮説
- グレースケール画像は、高強度が山の山を表し、低強度が谷を表す地形表面と考えることができます。
- それぞれの孤立した谷(極小値)を異なる色の水(ラベル)で満たします。
- 水位が上がると、さまざまな谷からの水が著しく合流し始め、近くの山(斜面)に応じて色が変化します。(異なるラベルマージの問題)
- これを回避するには、水が合流する場所にバリアを構築します。すべての山が水中になるまで、水でいっぱいになり、障害物を作り続けます。次に、作成したバリアがセグメンテーション結果を返します。
これがWatershedの背後にある「アイデア」です。WatershedのCMMWebページにアクセスして、いくつかのアニメーションを使用してそれについて学ぶことができます。- 新しい問題:しかし、このアプローチでは、画像のノイズやその他の不規則性のために、セグメント化された結果が生成されます。
- そのため、OpenCVはマーカーベースの流域アルゴリズムを実装しており、マージする谷のポイントとマージしない谷のポイントを指定できます。
- ===============================================
- これはインタラクティブな画像セグメンテーションです。私たちがしていることは、私たちが知っているオブジェクトに異なるラベルを割り当てることです。
- 前景またはオブジェクトであると判断した領域を1つの色(または強度)でマークし、背景または非オブジェクトであると判断した領域を別の色でマークし、最後
0
に不明な領域をマークします。これが私たちのマークです。- 次に、流域アルゴリズムを適用します。次に、マーカーが指定したラベルで更新され、オブジェクトの境界値はになります
-1
。コード
以下に、距離変換と流域を使用して、互いに接触しているオブジェクトをセグメント化する方法の例を示します。
コインが互いに接触している下のコインの画像を考えてみましょう。しきい値を設定しても、互いに接触します。
1.コインのおおよその見積もりを見つけることから始めます。したがって、大津の二値化を使用できます。
import numpy as np import cv2 as cv from matplotlib import pyplot as plt img = cv.imread('coins.png') gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY) ret, thresh = cv.threshold(gray,0,255,cv.THRESH_BINARY_INV+cv.THRESH_OTSU)
- 次に、画像内の白色点ノイズを除去する必要があります。このために、形態学的拡張を使用できます。
- オブジェクトの小さな穴を取り除くために、MorphologicalErosionを使用できます。
これで、オブジェクトの中心に近い領域が前景になり、オブジェクトの中心から遠い領域が背景になることを確認できます。私たちが確信していない唯一の領域は、コインの境界領域です。
- コインとして識別できる領域を抽出する必要があります。侵食は境界ピクセルを削除します。ですから、いくら残っていてもコインだと確信できます。これは、オブジェクトが互いに接触していない場合に機能します。ただし、それらは互いに接触しているため、別の適切なオプションは、距離変換を見つけて適切なしきい値を適用することです。
- コインではないと確信できるエリアを見つける必要があります。この目的のために、結果を拡張します。Inflateは、オブジェクトの境界を背景に追加します。このように、境界領域が削除されるため、結果の背景のすべての領域が実際に背景であると確信できます。下の画像を参照してください。
- 残りのエリアは、コインなのか背景なのか、私たちが知らないエリアです。流域アルゴリズムはそれを見つける必要があります。これらの領域は通常、前景と背景が出会うコインの境界の近くにあります(または2つの異なるコインですら)。それを境界と呼びます。面積から面積を引くことで求められます。
sure_bg
sure_fg
# 噪声去除 kernel = np.ones((3,3),np.uint8) opening = cv.morphologyEx(thresh,cv.MORPH_OPEN,kernel, iterations = 2) # 确定背景区域 sure_bg = cv.dilate(opening,kernel,iterations=3) # 寻找前景区域 dist_transform = cv.distanceTransform(opening,cv.DIST_L2,5) ret, sure_fg = cv.threshold(dist_transform,0.7*dist_transform.max(),255,0) # 找到未知区域 sure_fg = np.uint8(sure_fg) unknown = cv.subtract(sure_bg,sure_fg)
- 結果を表示します。しきい値処理された画像では、コインであると判断したコインの領域をいくつか取得し、それらを分離しました。(場合によっては、前景のセグメンテーションのみに関心があり、互いに接触しているオブジェクトを分離することには関心がない場合があります。その場合、距離変換を使用する必要はなく、侵食だけで十分です。侵食は、特定の前景を抽出するだけです。領域への別のアプローチ。 )。
これで、コインの領域と背景を判別できます。そこで、マーカー(元の画像と同じサイズですが、int32データ型を使用)を作成し、その中の領域にマークを付けます。確実にわかっている領域(前景か背景かを問わず)は、正の整数でマークされていますが、整数は異なりますが、不明な領域はゼロのままです。このために、** cv.connectedComponents **()を使用します。画像の背景を0でマークし、他のオブジェクトを1から始まる整数でマークします。
ただし、背景ラベルが0の場合、流域はそれを未知の領域として扱うことがわかっています。したがって、別の整数でラベルを付けたいと思います。代わりに、不明な定義を持つ不明な領域に0のラベルを付けます。
# 类别标记 ret, markers = cv.connectedComponents(sure_fg) # 为所有的标记加1,保证背景是0而不是1 markers = markers+1 # 现在让所有的未知区域为0 markers[unknown==255] = 0
JETカラーマップに示されている結果を参照してください。紺色の領域は未知の領域を示しています。もちろん、コインは色が違います。残りの、間違いなく背景の領域は、未知の領域と比較して明るい青で表示されます。
これでマーカーの準備が整いました。次に、分水界アルゴリズムを使用した最終ステップの時間です。次に、マーカー画像が変更されます。境界領域は-1でマークされます。
markers = cv.watershed(img,markers) img[markers == -1] = [255,0,0]
以下の結果を参照してください。一部のコインでは、接触する領域が正しくセグメント化されていますが、一部のコインではセグメント化されていません。