OpenCV – 空間フィルタリングと filter2D の使い方

目次

概要

画像処理の空間フィルタリングについて解説し、OpenCV の filter2D を使ったやり方を紹介します。

空間フィルタリング

入力画像の各画素に対して、対象の画素及びその近傍の画素の画素値の重み付き総和を計算し、出力画像の画素値を計算する処理を空間フィルタリング (spacial filtering) または畳み込み (convolution) といいます。 フィルタリングを行う際の重みはカーネル (kernel) またはフィルタ (filter) といいます。

入力画像の画素を $\text{src}(x, y)$、サイズが $N \times N$ のフィルタの値を $f(i, j)$ としたとき、出力画像の画素 $\text{dst}(x, y)$ は次のように計算できます。

$$ \text{dst}(x, y) = \sum_{i = 0}^N \sum_{j = 0}^N \text{src}(x + i, y + j) \times f(i, j) $$

フィルタリングの例

入力画像とカーネルは以下とします。

入力画像

カーネル

このとき、出力画像の各画素値はフィルタをスライドさせながら、カーネルと重なっている画素とカーネルの要素同士を乗算し、総和を取って計算します。

フィルタリングの例

cv2.filter2D

OpenCV では、cv2.filter2D() でフィルタリングが行えます。

dst = cv2.filter2D(src, ddepth, kernel[, dst[, anchor[, delta[, borderType]]]])
引数
名前 デフォルト値
src ndarray
入力画像
ddepth int
出力画像の型。-1 の場合は入力画像と同じ型を使用。
kernel ndarray
カーネル
anchor tuple of 2-ints (-1, -1)
フィルタを表す行列のアンカー成分。(-1, -1) の場合は行列の中心。
delta scalar 0
重み付き総和を計算したあと、この delta を足す
borderType BorderTypes cv2.BORDER_DEFAULT
パディング方法
返り値
名前 説明
dst 出力画像

サンプルコード

sample.jpg

In [1]:
import cv2
import numpy as np
from IPython.display import Image, display


def imshow(img):
    """ndarray 配列をインラインで Notebook 上に表示する。
    """
    ret, encoded = cv2.imencode(".jpg", img)
    display(Image(encoded))


# 画像を読み込む。
img = cv2.imread("sample.jpg", cv2.IMREAD_GRAYSCALE)

# 微分フィルタ
kernel = np.array([[0, 0, 0], [0, -1, 1], [0, 0, 0]])

# フィルタリングを行う。
dst = cv2.filter2D(img, -1, kernel)
imshow(dst)

ddepth – 出力画像の型

総和演算の結果は $[0, 255]$ の範囲に収まらない可能性があり、ddepth で指定した型で表せる範囲を超えた値はクリップされます。

意味
-1 入力画像と同じ型
cv2.CV_8U 符号なし8ビット
cv2.CV_8S 符号あり8ビット
cv2.CV_16U 符号なし16ビット
cv2.CV_16S 符号あり16ビット
cv2.CV_32S 符号あり32ビット
cv2.CV_32F 32ビット浮動小数点数
cv2.CV_64F 64ビット浮動小数点数
In [2]:
# エンボスフィルタ
kernel = np.array([[1, 0, 0], [0, 0, 0], [0, 0, -1]])

# ddeeth=-1
dst = cv2.filter2D(img, -1, kernel)
print("ddepth=-1 (cv2.CV_8U)", dst.min(), dst.max(), dst.dtype)

# ddeeth=cv2.CV_16S
dst = cv2.filter2D(img, cv2.CV_16S, kernel)
print("ddepth=-1 (cv2.CV_16S)", dst.min(), dst.max(), dst.dtype)
ddepth=-1 (cv2.CV_8U) 0 239 uint8
ddepth=-1 (cv2.CV_16S) -223 239 int16

delta – オフセット

重み付き総和を計算したあとに delta を足します。

In [3]:
# エンボスフィルタ
kernel = np.array([[1, 0, 0], [0, 0, 0], [0, 0, -1]])

dst = cv2.filter2D(img, -1, kernel)
imshow(dst)

dst = cv2.filter2D(img, -1, kernel, delta=128)
imshow(dst)

borderType – パディング方法

フィルタリングを行う際に、入力画像の端では画素が存在しない部分が出てくるので、その補完方法を指定します。

端の画素

意味
cv2.BORDER_CONSTANT 0でパディングする
cv2.BORDER_REPLICATE 一番端の値を繰り返してパディングする
cv2.BORDER_REFLECT 端で値を反転させてパディングする
cv2.BORDER_DEFAULT cv2.BORDER_REFLECT と同じ

入力画像

1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25
  • borderType=cv2.BORDER_CONSTANT

端は0でパディングします。(例: abcde -> 00|abcde|00)

0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 1 2 3 4 5 0 0
0 0 6 7 8 9 10 0 0
0 0 11 12 13 14 15 0 0
0 0 16 17 18 19 20 0 0
0 0 21 22 23 24 25 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
  • borderType=cv2.BORDER_REPLICATE

端の画素を繰り返してパディングします。(例: abcde -> aa|abcde|ee)

1 1 1 2 3 4 5 5 5
1 1 1 2 3 4 5 5 5
1 1 1 2 3 4 5 5 5
6 6 6 7 8 9 10 10 10
11 11 11 12 13 14 15 15 15
16 16 16 17 18 19 20 20 20
21 21 21 22 23 24 25 25 25
21 21 21 22 23 24 25 25 25
21 21 21 22 23 24 25 25 25
  • borderType=cv2.BORDER_REFLECT

端で折り返してパディングします。(例: abcde -> ba|abcde|ed)

7 6 6 7 8 9 10 10 9
2 1 1 2 3 4 5 5 4
2 1 1 2 3 4 5 5 4
7 6 6 7 8 9 10 10 9
12 11 11 12 13 14 15 15 14
17 16 16 17 18 19 20 20 19
22 21 21 22 23 24 25 25 24
22 21 21 22 23 24 25 25 24
17 16 16 17 18 19 20 20 19

コメント

コメントする

目次