OpenCV – cv2.distanceTransform で距離変換を行う方法

概要
OpenCV の cv2.distanceTransform() で距離変換を行う方法について解説します。
Advertisement
画像における距離の定義
2つの画素を $(x_1, y_1), (x_2, y_2)$ としたとき、距離関数としては次の種類があります。
- $L^\infty$ ノルム: $max(|x_1 – x_2|, |y_1 – y_2|)$
- $L1$ ノルム (マンハッタン距離): $|x_1 – x_2| + |y_1 – y_2|$
- $L2$ ノルム (ユークリッド距離): $\sqrt{(x_1 – x_2)^2 + (y_1 – y_2)^2}$
距離変換
距離変換 (distance transform) とは、2値画像を入力としたとき、各画素の最も近い画素値0までの距離を計算した 距離マップ (distance map) を作成する処理です。
cv2.distanceTransform
dst = cv2.distanceTransform(src, distanceType, maskSize[, dst[, dstType]])
引数
名前 | 型 | デフォルト値 |
---|---|---|
src | ndarray | |
入力画像 (1チャンネル) | ||
distanceType | DistanceTypes | |
距離関数の種類 * cv2.DIST_C: $L^\infty$ ノルム * cv2.DIST_L1: $L1$ ノルム (マンハッタン距離) * cv2.DIST_L2: $L2$ ノルム (ユークリッド距離) | ||
maskSize | int | |
距離変換に使用するマスクの大きさ | ||
dstType | DistanceTransformLabelTypes | cv2.DIST_LABEL_CCOMP |
値0の画素のラベル割当て方法 * cv2.DIST_LABEL_CCOMP: 0はすべて同じ値を割り当てる * cv2.DIST_LABEL_PIXEL: 0はすべて異なるラベルを割り当てる |
返り値
名前 | 説明 | ||
---|---|---|---|
edges | 出力画像 |
In [1]:
import cv2
import numpy as np
from IPython.display import Image, display
from matplotlib import pyplot as plt
def imshow(img):
"""ndarray 配列をインラインで Notebook 上に表示する。
"""
ret, encoded = cv2.imencode(".jpg", img)
display(Image(encoded))
# 画像を読み込む。
img = cv2.imread("sample1.jpg")
# グレースケールに変換する。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 2値化する
ret, bin_img = cv2.threshold(gray, 20, 255, cv2.THRESH_BINARY)
# 距離変換する。
dist = cv2.distanceTransform(bin_img, cv2.DIST_L2, 5)
fig, ax = plt.subplots(figsize=(6, 6))
ax.imshow(dist, cmap="magma")
plt.show()

Advertisement
応用例
2値画像でくっつている物体同士を分離したい場合などに活用できます。
In [2]:
# 画像を読み込む。
img = cv2.imread("sample2.jpg")
# グレースケールに変換する。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 2値化する
ret, bin_img = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
imshow(bin_img)

この段階では、コインの端がくっついているため、このあと輪郭抽出を行っても1枚1枚を別々の輪郭として認識できません。 ここで、距離変換で距離マップを作成し、距離に応じて2値化することで1枚ごとに分離します。
In [3]:
# 距離変換する。
dist = cv2.distanceTransform(bin_img, cv2.DIST_L2, 5)
fig, ax = plt.subplots(figsize=(6, 6))
ax.imshow(dist, cmap="magma")
plt.show()
# 距離を利用して2値化する。
ret, bin_img2 = cv2.threshold(dist, 0.5 * dist.max(), 255, cv2.THRESH_BINARY)
imshow(bin_img2)


参考文献
- デジタル画像処理 P191 9-2-8
-
前の記事
OpenCV – 画像を切り抜く、チャンネルを分離、結合する方法 2020.08.31
-
次の記事
OpenCV – ハフ変換 (Hough Transform) で直線を検出する方法 2020.09.01