Zhang-suen アルゴリズムによる亀裂グラフのスケルトン抽出

1. アルゴリズムの紹介

Zhang-suen は広く使用されている細線化アルゴリズムであり、スケルトン抽出アルゴリズムでもよく使用されるアルゴリズムです。

初期条件:入力はバイナリ イメージで、ゼロ点が背景を表し、ゼロ以外の値が前景を表します。このアルゴリズムの目的は、前景オブジェクトのエッジを洗練することです。

反復条件: Zhang-Suen アルゴリズムは、通常、変更が発生しなくなるまで交互に実行される 2 つの反復ステップで構成されます。2 つの手順は次のとおりです。

最初のステップでは、アルゴリズムは画像の各ピクセルを走査し、前景色が次の条件を満たしているかどうかを確認します。

  • 現在のピクセルは白です
  • 現在のピクセル P の上下、左右、左上、左下、右上、および右下の近傍には、少なくとも 1 つの黒いピクセルがあります。
  • 現在のピクセルの 8 近傍内の白いピクセルの数は 2 ~ 6 です。

上記の条件がすべて満たされている場合、現在のピクセルを黒としてマークします。

2 番目のステップでは、アルゴリズムは画像の各ピクセルを再度走査し、前景色が次の条件を満たしているかどうかを確認します。

  • 現在のピクセルは白です
  • 現在のピクセルに隣接する 8 つのピクセルのうち少なくとも 1 つは黒です
  • 現在のピクセルの 8 近傍内の白いピクセルの数は 2 ~ 6 です。
  • 現在のピクセルの 8 近傍内の黒ピクセルは接続されていません (つまり、接続成分ではありません)。

上記の条件がすべて満たされている場合、現在のピクセルを黒 (背景) としてマークします。

反復終了条件:ピクセルを削除できなくなるまで、反復ステップ 1 と 2 が交互に実行されます。1 回の反復後にピクセルが削除されない場合、アルゴリズムは終了します。そうでない場合は、次の反復に進みます。

結果:複数回の反復の後、Zhang-Suen アルゴリズムは画像内の白いターゲット オブジェクトのエッジを洗練し、最終的に 1 ピクセル幅のスケルトンを生成します。

具体的な実装はプログラミング言語や画像処理ライブラリによって異なる場合がありますが、上記では Zhang-Suen アルゴリズムの基本原理と手順を説明しています。

手書きアルゴリズムを選択したい場合は、次の 2 つの記事を参照してください。

レーザーストライプ中心抽出——Zhang-Suen法Python

ビジュアルグループ学習コンテンツ:Zhang-Suen骨格抽出アルゴリズム

2.skimageはスケルトン抽出を実装します

私も過去に手書きのものを使用したことがありますが、そのほとんどには依然として論理エラーや実行タイムアウトなどがあり、その後の調査の結果、zhang-suen アルゴリズムが skimage の形態モジュールに実装されていることがわかりました。画像は二値画像であることをここで使用していますが、閾値を自動的に選択して画像を二値化し、画像を前景と背景に分離するのが大津閾値法です。

from skimage.filters import threshold_otsu,median
from skimage.morphology import skeletonize,dilation,disk
from skimage import io, morphology

import matplotlib.pyplot as plt
plt.switch_backend('TkAgg')
# 使用Otsu阈值方法进行二值化处理

path = r"D:\PythonProject\RoadCrack\dimension2_data\num\001.png"

image = io.imread(path, as_gray=True)
thresh = threshold_otsu(image)
binary = image > thresh
skeleton = skeletonize(binary)
io.imshow(skeleton)
# io.imsave('output.png', skeleton)
io.show()

原画:

実行結果: 

画像にはまだいくつかの欠陥があることがわかります。画像の前処理を使用すると、画像のバリやノイズを軽減し、画像の品質を向上させることができます。

私自身のデータセットでのテストによると、膨張、メディアン フィルタリング、クロージング操作などのアルゴリズムを使用すると、画像の欠陥を効果的に減らすことができます。

私はクラック フォレスト データ セットを使用しています。他の形態学的手法をいくつか試して、自分のデータ セットに適した組み合わせをできるだけ早く見つけるようにしてください。以下はテスト後に得られた組み合わせであり、誰でも学ぶことができます。 。

from skimage.filters import threshold_otsu,median
from skimage.morphology import skeletonize,dilation,disk
from skimage import io, morphology

import matplotlib.pyplot as plt
plt.switch_backend('TkAgg')
# 使用Otsu阈值方法进行二值化处理

path = r"D:\PythonProject\RoadCrack\dimension2_data\num\001.png"

image = io.imread(path, as_gray=True)
thresh = threshold_otsu(image)
binary = image > thresh

binary = dilation(binary, disk(3))
binary = median(binary, selem=morphology.disk(5))
binary = dilation(binary, disk(2))
binary = median(binary, selem=morphology.disk(5))
# 添加闭运算
selem = morphology.disk(3)
binary = morphology.closing(binary, selem)

skeleton = skeletonize(binary)

io.imshow(skeleton)
# io.imsave('output.png', skeleton)
io.show()

3. データセットの問題

インターネット上でテスト用のオープン ソース データ セットを見つけることができます。私は Crack Forest データ セットを使用しています。Kaggleでクラックフォレスト | Kaggleを見つけることができます。ダウンロードしたデータ セットに mat サフィックスが付いている場合は、次のスクリプトを使用して変換できます。

# Mat2png.py

from os.path import isdir
from scipy import io
import os, sys
import numpy as np
from PIL import Image

if __name__ == '__main__':
    file_path = './groundTruth/'
    png_img_dir = './groundTruthPngImg/'
    if not isdir(png_img_dir):
        os.makedirs(png_img_dir)
    image_path_lists = os.listdir(file_path)
    images_path = []
    for index in range(len(image_path_lists)):
        image_file = os.path.join(file_path, image_path_lists[index])
        # print(image_file)#./CrackForest-dataset-master/groundTruth/001.mat
        images_path.append(image_file)
        image_mat = io.loadmat(image_file)
        segmentation_image = image_mat['groundTruth']['Segmentation'][0]
        segmentation_image_array = np.array(segmentation_image[0])
        image = Image.fromarray((segmentation_image_array - 1) * 255)
        png_image_path = os.path.join(png_img_dir, "%s.png" % image_path_lists[index][0:3])
        image.save(png_image_path)

変換された画像には、次の問題がまだ残っている可能性があります。私が取得したクラック フォレスト データ セットは標準のバイナリ画像ではありません。注意していなければ、ラベルに問題は見つからなかったでしょう。

 遠目で見る分には問題ありませんが、画像ビューアやPSで拡大すると以下のようになります。

この種の画像は問題があるため、2値化する必要があります。もちろん、グレースケールに変換した後にしきい値法を使用することもできます。

std = 127.5
mask[mask > std] = 255
mask[mask < std] = 0
mask = mask.astype("uint8")

おすすめ

転載: blog.csdn.net/m0_62919535/article/details/133211055