入力した任意の図形から、その図形の1px内側の図形を抽出する。
イメージはこう。少し見づらいですが外側が赤、内側が緑になっています。
今回は距離変換を用いて内側の図形を取得します。
パラメータ
- 画像データ
画像データの前提条件は下記。- 閉じた図形である。
- 図形は一つだけ描かれている。
- 二値画像である。
距離変換
OpenCv(Sharp)にはDistanceTransform()
という距離変換を行う便利関数があるのでこれを使う。
Mat
を入力にMat<float>
を返し、このfloat
がその画素における距離である。
DistanceTransform()
には二つパラメータがある。
-
DistanceType:
距離計算方法。主にL1, L2, Cを使う。- DistanceType.L1: 市街地距離
- DistanceType.L2: ユークリッド距離
- DistanceType.C: チェス盤距離
- DistanceType.L12: L1とL2の中間の性質を持つ距離
- DistanceType.Fair, Welsch, Huber: いずれも外れ値に対して頑健な距離計算
-
DistanceTransformMasks:
距離計算に用いるマスクサイズ。マスクサイズが大きいほど複雑な形状に対応した距離変換になる- DistanceTransformMasks.Mask3: 3*3の範囲をマスク
- DistanceTransformMasks.Mask5: 5*5の範囲をマスク
- DistanceTransformMasks.Precise: より精密な計算のためのマスク
- ドキュメントではまだサポートされていないとあるが、エラーにはならず動くのでMask3かMask5に変換されているのか?
今回は市街地距離で計算し、複雑な図形を扱わないことからMask3を採用する。
最終的なコード
下記では距離変換で得られた距離をインデックスとして、色付けして見やすくしている。
public void ShowDistanceColor(Mat binaryImage)
{
// 距離変換を適用する
var dist = binaryImage.DistanceTransform(DistanceTypes.C, DistanceTransformMasks.Mask3);
dist.MinMaxLoc(out double _, out var maxVal);
// 出力画像の準備
var output = binaryImage.CvtColor(ColorConversionCodes.GRAY2BGR);
var random = new Random();
// 距離値ごとに色を変えて描画する
for (var distance = 0; distance <= (int) maxVal; distance++)
{
var color = new Vec3b(
(byte) random.Next(256),
(byte) random.Next(256),
(byte) random.Next(256));
for (var y = 0; y < dist.Rows; y++)
for (var x = 0; x < dist.Cols; x++)
{
if ((int) dist.At<float>(y, x) != distance) continue;
// 好きなように処理する
output.Set(y, x, color);
}
}
Cv2.ImShow("Color Coded Distance Map", output);
Cv2.WaitKey(0);
}
まとめ
画像処理のアルゴリズムって勉強していると「いつ使うんだろ?」と思うこと多いけどふとした時に使う場面と出くわしますね。