web-dev-qa-db-ja.com

numpy配列の円形マスクを作成するにはどうすればよいですか?

Pythonで画像を循環マスクしようとしています。ウェブ上でいくつかのサンプルコードを見つけましたが、数学を変更して円を正しい場所に配置する方法がわかりません。

タイプimage_data、形状numpy.ndarrayの画像(3725, 4797, 3)があります:

total_rows, total_cols, total_layers = image_data.shape
X, Y = np.ogrid[:total_rows, :total_cols]
center_row, center_col = total_rows/2, total_cols/2
dist_from_center = (X - total_rows)**2 + (Y - total_cols)**2
radius = (total_rows/2)**2
circular_mask = (dist_from_center > radius)

このコードはdist_from_centerの計算にユークリッド距離を適用しているようですが、X - total_rowsY - total_colsの部分がわかりません。これにより、画像の左上を中心とする円の4分の1のマスクが生成されます。

XYはどのような役割を果たしていますか?そして、どのようにこのコードを変更して、代わりに画像内のどこかに中心を置くマスクを生成できますか?

12
user7469692

あなたがオンラインにしたアルゴリズムは、少なくともあなたの目的のために、部分的に間違っています。次の画像がある場合は、次のようにマスクする必要があります。

Image to maskMasked image

このようなマスクを作成する最も簡単な方法は、アルゴリズムがどのように処理するかですが、希望どおりに表示されず、簡単な方法でマスクを変更することもできません。画像内の各ピクセルの座標を見て、そのピクセルが半径内にあるかどうかのtrue/false値を取得する必要があります。たとえば、次の図は、円の半径とその半径内に厳密に含まれていたピクセルを示す拡大図です。

Pixels inside the radius

次に、どのピクセルが円の内側にあるかを把握するために、画像内の各ピクセルのインデックスが必要になります。関数 np.ogrid() は2つのベクトルを提供し、それぞれにピクセルの位置(またはインデックス)が含まれます。列インデックスの列ベクトルと行インデックスの行ベクトルがあります。

_>>> np.ogrid[:4,:5]
[array([[0],
       [1],
       [2],
       [3]]), array([[0, 1, 2, 3, 4]])]
_

この形式は broadcasting に役立ちます。特定の関数で使用すると、実際には2つのベクトルだけでなくすべてのインデックスのグリッドが作成されます。したがって、np.ogrid()を使用して画像のインデックス(またはピクセル座標)を作成し、各ピクセル座標をチェックして、円の内側か外側かを確認できます。それが中心の内側にあるかどうかを知るために、中心からすべてのピクセル位置までのユークリッド距離を見つけることができます。その距離が円の半径よりも小さい場合は、includedをマスクに含め、それよりも大きい場合は、マスクからexcludeします。

これで、このマスクを作成する関数を作成するために必要なものがすべて揃いました。さらに、Nice機能を少し追加します。中心と半径を送信するか、自動的に計算させることができます。

_def create_circular_mask(h, w, center=None, radius=None):

    if center is None: # use the middle of the image
        center = [int(w/2), int(h/2)]
    if radius is None: # use the smallest distance between the center and image walls
        radius = min(center[0], center[1], w-center[0], h-center[1])

    Y, X = np.ogrid[:h, :w]
    dist_from_center = np.sqrt((X - center[0])**2 + (Y-center[1])**2)

    mask = dist_from_center <= radius
    return mask
_

この場合、_dist_from_center_は、指定されたのと同じ高さと幅の行列です。列と行のインデックスベクトルを行列にブロードキャストします。各位置の値は中心からの距離です。このマトリックスを画像として視覚化すると(適切な範囲にスケーリング)、指定した中心から放射状に広がるグラデーションになります。

Distance gradient

したがって、これをradiusと比較すると、このグラデーションイメージのしきい値処理と同じです。

最後のマスクはブール値の行列であることに注意してください。 Trueその場所が指定された中心からの半径内にある場合はFalse、それ以外の場合。したがって、このマスクを、関心のあるピクセルの領域のインジケータとして使用するか、そのブール値(_~_ in numpy)の反対を取り、その領域の外側のピクセルを選択できます。したがって、この関数を使用して、円の外側のピクセルを黒に着色することは、この投稿の冒頭で行ったように、次のように簡単です。

_h, w = img.shape[:2]
mask = create_circular_mask(h, w)
masked_img = img.copy()
masked_img[~mask] = 0
_

しかし、中心とは異なる点に円形マスクを作成したい場合は、それを指定できます(関数は、_[x,y]_のインデックスではなく、_[row,col] = [y,x]_の順番で中心座標を想定しています):

_center = [int(w/4), int(h/4)]
mask = create_circular_mask(h, w, center=center)
_

これは、半径を指定していないため、最小の半径が得られるため、円が画像の境界に収まるようになります。

Off-center mask

または、中心を計算させて、指定した半径を使用することもできます。

_radius = h/4
mask = create_circular_mask(h, w, radius=radius)
_

最小の寸法に正確に拡張しない半径の中心にある円を与える:

Specified radius mask

そして最後に、画像の境界の外に広がる半径を含めて、必要な任意の半径と中心を指定できます(中心は画像の境界の外にあることもできます!):

_center = [int(w/4), int(h/4)]
radius = h/2
mask = create_circular_mask(h, w, center=center, radius=radius)
_

Off-center, larger radius mask

オンラインで見つけたアルゴリズムが行うことは、中心を_[0,0]_に設定し、半径をhに設定することと同じです。

_mask = create_circular_mask(h, w, center=[0,0], radius=h)
_

Quarter-circle mask

25
alkasm