穴のある非凸多角形の最小壁厚


9

穴を含む複雑な非凸多角形領域の最小壁厚(値と場所)を見つける最も効率的な方法は何ですか?最小の壁の厚さが赤である、青色のポリゴンの例を参照してください。ただし、この場合、隣接する2本の線が平行である場合、位置は不明確です。

ここに画像の説明を入力してください

これまでのところ、私たちは試しました:

  • ポリゴンラインを細分割し、ポリゴン内の最小ポイント-ポイントラインを検出する(強引、10'000ポイントを超える複雑なポリゴンでは効率的ではない)
  • Delaunay三角形分割とポリゴン内部の最小エッジの検出。正確性は十分ではありません。最初にポリゴンラインのサブディビジョンと組み合わせる場合にのみ可能です。次の例(Nr 3)は、Delaunay三角形分割では、シンプレックスエッジが赤で検出されず、緑のボックスの最小の壁の厚さを見落とす場合です。
    ここに画像の説明を入力してください
  • 侵食バッファを繰り返し増やして最小のはめ込みを見つけます。この場合、侵食ポリゴンは複数の部分に分割されます=最小壁厚の半分。問題は、後でこのアプローチで最小肉厚の場所を見つけることです。さらに、侵食は常に複数の部分に分かれているわけではなく、「行き止まり」を見逃しています。これは、線まで侵食し、誤った最小壁厚を与える例(Nr 2)です。
    ここに画像の説明を入力してください
  • 最初に中心軸を見つけてから、多角形領域を覆っているが重なっていない中心軸上の最小円を検索します。編集:問題のあるのは、中心軸上の多くの「間違った候補者」です。(Nr 1)円Aは間違っています。円Bは正しい最小壁厚を示します。
    ここに画像の説明を入力してください

すべてのラインペア間の距離を取得して、最も近いラインペアを見つけます。
bugmenot123 2018

では、内側軸アプローチの何が問題でしたか?
Hornbydd 2018

1
@Hornbydd:問題は、内側の軸にコーナーに接しているが壁の厚さを定義していない円が多数あることでした。2番目の例を参照してください 。円Aは間違っており、円Bは最小肉厚の正しい位置です。したがって、内側の軸は計算上コストのかかる回り道のように見え、正しい答えを提供していない...
Oliver Staubli 2018

1
ポリゴンが縮退して点に接する2つのポリゴンになるまで侵食を行うと、その位置は、その点を中心とするバッファーと同じ半径の円が元のポリゴンに接する位置になります。それは証明なしで提示された仮説ですが、反例を見ることができません...
Spacedman

1
@OliverStaubli私の提案は、ドロネー三角形のエッジだけでなく、境界に1つのエッジがあり、ポリゴンの内部に他の2つのエッジがある三角形の高さもチェックすることです。例Nr.3では、緑色の正方形の下の三角形の高さが探しているものです。(三角形分割の制約によっては、鈍角三角形の候補の一部を
除外する

回答:


1

穴を含む複雑な非凸多角形領域の最小壁厚(値と位置)を見つける最も効率的な方法の1つは、最初に最も近いセグメントを決定するために、点の等間隔のレイヤー(またはランダム)を使用することです。各ポイントのコンテキスト、次にインクリメンタルセグメントと反対側のポリゴンの間の交点。監督コサインに基づいています。

増分距離は、最初のセグメントが一部の側面ポリゴンに到達して交差するまで使用できます(最小の壁の厚さ)。

私のアプローチを試すために、穴のあるポリゴンを複製し、100ポイントのポリゴン内にランダムポイントレイヤーを作成しました。次の画像で確認できるように:

ここに画像の説明を入力してください

PyQGISで使用されるコードは次のようになります。

import math

def azimuth(point1, point2):
   return point1.azimuth(point2) #in degrees

def cosdir_azim(azim):
   azim = math.radians(azim)
   cosa = math.sin(azim)
   cosb = math.cos(azim)
   return cosa,cosb

registry = QgsMapLayerRegistry.instance()    
polygon = registry.mapLayersByName('polygon_with_holes')
point_layer = registry.mapLayersByName('Random_points')
points = [ feat.geometry().asPoint() for feat in point_layer[0].getFeatures() ]
feat_polygon = polygon[0].getFeatures().next()

#producing rings polygons
rings_polygon = feat_polygon.geometry().asPolygon()
segments = []
epsg = point_layer[0].crs().authid()
uri = "LineString?crs=" + epsg + "&field=id:integer""&index=yes"
mem_layer = QgsVectorLayer(uri,
                           'increasing_segments',
                           'memory')
prov = mem_layer.dataProvider()
length = 10
pt2 = 0 
k = 0
while pt2 == 0:
    for i, point in enumerate(points):
        #determining closest distance to vertex or side polygon
        dist1 = feat_polygon.geometry().closestSegmentWithContext(point)[0]
        #determining point with closest distance to vertex or side polygon
        pt = feat_polygon.geometry().closestSegmentWithContext(point)[1]
        cosa, cosb = cosdir_azim(azimuth(pt, point))
        #extending segment in opposite direction based in director cosine and length 
        op_pt  = QgsPoint(point.x() + (length*cosa), point.y() + (length*cosb))
        segments.append([pt,op_pt])
        geom = QgsGeometry.fromPolyline([point,op_pt])
        for ring in rings_polygon:
            geom_ring = QgsGeometry.fromPolyline(ring)
            if geom.intersects(geom_ring):
                pt3 = geom.intersection(geom_ring)
                pt2 = pt3.distance(QgsGeometry.fromPoint(point))
                ms = [pt3.asPoint(), pt]
    length += 100
    k += 1
new_segments = segments[len(segments) -1 - len(segments)/k: len(segments) - 1]

feats = [ QgsFeature() for i in range(len(new_segments)) ]
for i,feat in enumerate(feats):
    feat.setAttributes([i])
    geom = QgsGeometry.fromPolyline(new_segments[i])
    feat.setGeometry(geom)

prov.addFeatures(feats)
QgsMapLayerRegistry.instance().addMapLayer(mem_layer)
minimum_segment = QgsGeometry().fromPolyline(ms).exportToWkt()
print minimum_segment, k

また、増分距離のメモリレイヤーを作成し(視覚化の目的のみ)、WKT形式で最小の壁厚を印刷します。

QGISのPythonコンソールでコードを実行した後、次の画像の結果が得られました。

ここに画像の説明を入力してください

予想される領域では、最初に反対側に到達した距離が1つだけ増加したことがわかります。

印刷されたWKT形式(最小の壁厚のため)は、QGISのQuickWKTプラグインで使用され、次の画像でそのセグメントを視覚化します。

ここに画像の説明を入力してください

「コンテキストに最も近いセグメント」が頂点に結合されたため、わずかな傾きが生じました。代わりにサイドポリゴン。ただし、コード例外またはより多くのポイントで回避できます。


1

ポットに投入する2つのアイデア:

  1. ポリゴンをラスタライズし、距離変換を使用します(ゼロ以外の各ピクセルからゼロピクセルまでの最短距離の画像を返します)。ラスタライズされた画像をスケルトン化し、スケルトンに沿って距離変換された画像の値を取得します。そのセットから、あなたの最も狭い幅に対応するいくつかの最小値があります。このセットは、ブルートフォースアプローチを実装するための初期検索ポイントとして使用できます。スケルトンはオブジェクトのコーナーを二等分し、それらの場所では距離変換がゼロに近づくことに注意してください(オブジェクトの境界に近づくにつれて)。これは問題があるかもしれませんが、あなたの問題の問題を表しています-なぜできますか 最小幅はコーナーにありますか?2つのポイント間の境界の最短距離にしきい値を設定することで、この問題に対処できます(それらが同じオブジェクト上にある場合)。周辺ピクセルのセットに対して測地線距離変換を使用して、その値をすばやく見つけることができます。

    この方法では、ラスタライズされたポリゴンの解像度を決定する必要があります。これにより、ある程度のスケール依存性が生じます。また、高すぎる解像度を選択すると、距離の変換に時間がかかる場合があります。しかし、一般的にはかなり高速です。この方法では必要な精度が得られない可能性がありますが、少なくとも、チェックする必要のある場所のセットがはるかに少なくなる可能性があります。

  2. あなたのブルートフォースメソッドは、開始するには悪い場所ではありません。(長い)ラインとの交点をすべて見つける必要があるという同様の問題があり、kdツリー検索アルゴリズムを使用して検索時間を大幅に高速化することができました(当時はMatlabで範囲検索を使用していました)。最初に近隣内のポイント。そうすれば、ポイントの合計数のごく一部を総当たりにするだけです。


@jonに感謝します。両方の有望なアプローチ。私はすでにkdTreeを検討していましたが、説明されている問題によく知られている「コピーと貼り付け」の解決策があることを願っています:-)より深く掘り下げる必要があります...
Oliver Staubli

0

内側の軸のアプローチは正しく、悪い円を無視するための基準が必要です。内側の軸の各円は、2つ(またはそれ以上)の点で表面に接触しています。円の中心から表面上のこれらの点までのベクトルを想像して、その間の角度が良い円Bの場合は180°、悪い円Aの場合は90°にすぎないことを確認してください。

ここに画像の説明を入力してください


0

一般的なポリゴン描画アルゴリズムは、線分を上から下に並べ替え、それらを介してピクセルの直交線を描画することで機能します。ポリゴンを(曲率なしで)分解するこの方法は非常に高速なので、これを基礎として使用できます。上から下へ行くだけでなく、0、30、60、90度で最短の直交断面(=最小の壁の厚さ)を見つけることができます。これは、ポイントごとに1回だけ計算する必要があります。あらゆる種類の「ピクセル解像度」。

computer-graphics-scan-line-polygon-fill-algorithmを参照してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.