本日はラプラシアンフィルタという画像のディジタルフィルタを紹介し、ラプラシアンフィルタの効果であるエッジの抽出を行うプログラムを紹介してみたいと思います。
ラプラシアンフィルタとは
ラプラシアンフィルタ(Laplacian Filter)は、二次微分を利用して画像から輪郭を抽出するフィルタです。
二次微分というと複雑に感じますが、以下のような係数で与えられるディジタルフィルタです。対象画素の周囲が「1」となり、中央の画素が4近傍であれば「-4」、8近傍であれば「-8」になります。

このフィルタを用いることでエッジの抽出が可能です。
上の図の赤枠の中のように、全ての画素値が同じ領域では、出力される画素の値は0になります。
一方、上の図の緑枠の中のように、対象画素と周囲画素に差がある場合には、出力される画素の値の絶対値が大きくなります。
結果として、ラプラシアンフィルタを用いると画像のエッジ抽出が可能となります。
ラプラシアンフィルタは何故二次微分フィルタと呼ばれるのか
何故ラプラシアンフィルタは二次微分フィルタと呼ばれるのでしょうか。
まず、微分とは何かですが、これはある関数が与えられた際に、簡単に言えば勾配(傾き)を求める行為ですよね。
例えば
ラプラシアンフィルタが二次微分フィルタと呼ばれるのは、ラプラシアンフィルタは傾きの傾きを求め、それを数値化した値を算出するフィルタだからです。
以下の図で例を見てみましょう。ラプラシアンフィルタをある画像に掛ける際のラプラシアンフィルタのフィルタ係数は、画像の画素値の傾きの傾きを求める場合に等しくなります。

上の画像は横方向のみの傾きの傾き(二次微分)を求めていますが、縦方向も同時に求めて総和を出すと、これは4近傍のラプラシアンフィルタのフィルタ係数の値となります。

すなわち、ラプラシアンフィルタはある対象画素の周囲の画素値の、傾きの傾き(二次微分)を求め、その値を出力しているフィルタであると言えると思います。
画像へのラプラシアンフィルタ適用によるエッジ抽出プログラム(Python+OpenCV)
ソースコード
画像にラプラシアンフィルタを適用するプログラムを以下で紹介します。
出力画素値に対して絶対値を取るか否か(取らない場合は、負の値は全て0として扱う)については、どちらがいいかは諸説あるので、今回は両方の出力を確認できるようにしました。
また、4近傍を用いたラプラシアンフィルタと、8近傍を用いたラプラシアンフィルタの双方を試せるようにしました。
動作環境:OpenCV 4.5.5
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import numpy as np
import cv2
import sys
def main():
print("OpenCV Version: " + str(cv2.__version__))
# Loading image data (GRAYSCALE)
filename = "image.png"
image = cv2.imread(filename, cv2.IMREAD_GRAYSCALE)
if image is None:
print("Cannot find image data : " + filename)
sys.exit()
# Laplacian Filter (4 neighbors)
kernel_lap4 = np.array([[0, 1, 0],
[1, -4, 1],
[0, 1, 0]])
image_lap4 = cv2.filter2D(image, cv2.CV_64F, kernel_lap4)
image_lap4_abs = np.uint8(np.abs(image_lap4))
# Laplacian Filter (8 neighbors)
kernel_lap8 = np.array([[1, 1, 1],
[1, -8, 1],
[1, 1, 1]])
image_lap8 = cv2.filter2D(image, cv2.CV_64F, kernel_lap8)
image_lap8_abs = np.uint8(np.abs(image_lap8))
# Saving image
cv2.imwrite('image_laplacian_4.png',image_lap4)
cv2.imwrite('image_laplacian_4_abs.png',image_lap4_abs)
cv2.imwrite('image_laplacian_8.png',image_lap8)
cv2.imwrite('image_laplacian_8_abs.png',image_lap8_abs)
if __name__ == "__main__":
main()
入力データとしては、以下の画像を用いました。

プログラムの動作結果
以下の4パターンの出力を比較したいと思います。
- 4近傍ラプラシアンフィルタ(出力画素値に絶対値をつけず、負の値は0にして出力する)
- 4近傍ラプラシアンフィルタ(出力画素値に絶対値をつける)
- 8近傍ラプラシアンフィルタ(出力画素値に絶対値をつけず、負の値は0にして出力する)
- 8近傍ラプラシアンフィルタ(出力画素値に絶対値をつける)
4近傍ラプラシアンフィルタ(出力画素値に絶対値をつけない)

4近傍ラプラシアンフィルタ(出力画素値に絶対値をつける)

8近傍ラプラシアンフィルタ(出力画素値に絶対値をつけない)

8近傍ラプラシアンフィルタ(出力画素値に絶対値をつける)

8近傍の方がエッジが鮮明に出力される印象があります。また、絶対値をつける場合ではエッジの線が少しボケたような印象になってしまうため、絶対値を付けなくても良いかもしれません。
まとめ
本日はラプラシアンフィルタを用いたエッジ抽出を紹介しました。以前にPrewittフィルタやSobelフィルタを用いたエッジ抽出についても紹介しているので、以下の記事も参考にしてみてください。