はじめに
エッジ検出は画像処理の基本的なタスクで、物体の輪郭や特徴を強調するために利用されます。
エッジ検出は複数のアルゴリズムが提案されていて、本サイトでもCannyエッジ検出器、ソーベルフィルターによるエッジ検出を紹介しました。
この記事では、OpenCVを使用したラプラシアン フィルター(Laplacian Filter)を用いたエッジ検出の原理と実装方法を詳しく説明します。初心者にも分かりやすく、サンプルコードを交えながら、ラプラシアン フィルターの特性を確認します。さらに、Cannyエッジ検出器、ソーベルフィルター、プリウィット フィルターとの比較を通じて、それぞれの特徴、利点、制約についても詳しく紹介します。
ラプラシアン フィルターとは
ラプラシアン フィルターは、画像中の急激な輝度変化(エッジ)を検出するために使用されるフィルターです。このフィルターは画像中の輝度勾配を計算し、勾配が急激な場所をエッジとして検出します。ラプラシアン フィルターは、画像中の二次微分を計算することによってエッジ情報を抽出します。
特徴
- エッジが急激な部分を強調するのに優れています。
- シンプルで計算が高速です。
- パラメータ調整が比較的簡単です。
- ノイズに対して敏感なため、ノイズを強調することがあります。したがって、前処理が必要となる場合があります。
- 画像中の小さなエッジを検出することが苦手です。
ラプラシアン フィルターは、以下の手順で計算されます。
- 画像のグレースケール変換
まず、ラプラシアン フィルターは通常、カラー画像ではなく、グレースケール画像に適用されます。これは、色情報を無視し、輝度情報だけを考慮することでエッジを抽出しやすくするためです。
- 離散ラプラシアン演算子
ラプラシアン フィルターは、ラプラシアン演算子(Δ)を使用します。ラプラシアン演算子は、以下の数式で表されます。
$$\nabla^2I(x,y) = \frac{\partial^2}{\partial{x}^2}I+ \frac{\partial^2}{\partial{y}^2}I$$
ここで、\(I(x,y) \)は画像の輝度値です。この演算子は画像の各ピクセルに対して、x軸とy軸の両方の方向に対する二次微分を計算します。これにより、画像中の急激な変化を強調し、エッジが浮かび上がります。 - 離散化
コンピュータ上で画像を処理するために、連続的な微分演算子を離散的なフィルターカーネルに変換する必要があります。ラプラシアン演算子を離散化すると、以下のようになります.
$$\Delta I(x,y)=I(x+1,y)+I(x−1,y)+I(x,y+1)+I(x,y−1)−4I(x,y)$$
この式では、中心ピクセル (x, y) の輝度値から周囲の4つのピクセルの輝度値を引いています。これにより、中心ピクセルとその周囲の輝度の違いが強調され、急峻な輝度変化(エッジ)が抽出されます。 - フィルターカーネルを使用
ラプラシアン フィルターは、上記の離散化されたラプラシアン演算子をフィルターカーネルとして使用して画像に畳み込むことで適用されます。畳み込み操作は、画像の各ピクセルにフィルターカーネルを適用し、中心ピクセルの値を計算します。 - 絶対値の取得
ラプラシアン フィルターの出力は負の値を含むことがあります。通常、絶対値を取得してエッジの輪郭を強調し、正の値のエッジも検出します。
ラプラシアン フィルターは、急激な輝度変化を持つエッジを抽出するのに非常に効果的ですが、ノイズに対しても敏感であるため、スムージングなどの前処理が必要なことに注意してください。
cv2.Laplacian関数
OpenCVでラプラシアン フィルターを適用する場合はcv2.Laplacian
関数を使用します。
■(広告)OpenCVの参考書としてどうぞ!■
引数
名称 | 説明 |
入力画像(必須) | 入力画像データ。グレースケール画像を対象とします。カラー画像を使用する場合、事前にグレースケールに変換する必要があります。 |
ddepth(必須) | 出力画像のデータ型を指定します。出力画像のデータ型を指定することで、演算の精度を調整できます。指定できるものは、こちらで確認できます。 |
ksize(オプション) | ラプラシアン カーネルのサイズを指定します。カーネルサイズが大きいほど、エッジが滑らかになります。(デフォルト:1) |
scale(オプション) | ラプラシアン演算の結果に掛けるスケールファクターを指定します。通常、1を指定しますが、値を調整してエッジの強調度を変更することができます。(デフォルト:1) |
delta(オプション) | 処理結果に追加するオフセットを指定します。通常、0を指定しますが、値を変更することで画像の輝度を調整できます。(デフォルト:0) |
borderType(オプション) | 画像の境界条件を指定します。(デフォルト:cv2.BORDER_DEFAULT ) |
戻り値
戻り値は検出されたエッジを表すマップ(画像)。
使い方
ラプラシアン フィルターのサンプルコードは、カラー画像ではなくグレースケール画像を対象としています。
入力画像として、この画像を使用しています。
cv2.imread
関数でグレースケールに変換しました。引用元
https://pixabay.com/ja/photos/%E5%BB%BA%E7%89%A9-%E6%A9%8B-%E5%B7%A5%E4%BA%8B-%E5%AF%BE%E7%A7%B0-5506574/
下がサンプルコードです。
このサンプルコードでは、引数のddepth
をcv2.CV_8Uとcv2.CV_32F、ksize
を3と5の場合についてエッジ検出し、結果を比較しています。
また、入力画像のスムージングにはガウシアンフィルターを使用しています。
import cv2 import matplotlib.pyplot as plt import numpy as np # 画像の読み込み image = cv2.imread("image.jpg", cv2.IMREAD_GRAYSCALE) # ガウシアンフィルターを適用してノイズを削除 kernel_size = 5 # カーネルサイズの設定 sigma = 0 # sigmaの設定 blurred = cv2.GaussianBlur(image, (kernel_size, kernel_size), sigma) # ガウシアンフィルターの適用 # ラプラシアン フィルターを適用 laplacian_8U_k3 = cv2.Laplacian(blurred, cv2.CV_8U, ksize=3) laplacian_8U_k5 = cv2.Laplacian(blurred, cv2.CV_8U, ksize=5) laplacian_32F_k3 = cv2.Laplacian(blurred, cv2.CV_32F, ksize=3) laplacian_32F_k5 = cv2.Laplacian(blurred, cv2.CV_32F, ksize=5) # データ型をcv2.CV_32F(float32)からcv2.CV_8U(uint8)に変換 laplacian_32F_k3 = np.clip(laplacian_32F_k3 * 255, a_min = 0, a_max = 255).astype(np.uint8) laplacian_32F_k5 = np.clip(laplacian_32F_k5 * 255, a_min = 0, a_max = 255).astype(np.uint8) # 絶対値を取得 laplacian_8U_k3 = cv2.convertScaleAbs(laplacian_8U_k3) laplacian_8U_k5 = cv2.convertScaleAbs(laplacian_8U_k5) laplacian_32F_k3 = cv2.convertScaleAbs(laplacian_32F_k3) laplacian_32F_k5 = cv2.convertScaleAbs(laplacian_32F_k5) # 画像とエッジ画像を表示 plt.rcParams["figure.figsize"] = [9,9] # ウィンドウサイズを設定 title = "cv2.Laplacian: codevace.com" plt.figure(title) # ウィンドウタイトルを設定 plt.subplots_adjust(left=0.05, right=0.95, bottom=0.03, top=0.95) # 余白を設定 plt.subplot(321) # 3行2列の1番目の領域にプロットを設定 plt.imshow(image, cmap='gray') # 入力画像をグレースケールで表示 plt.title('Original Image') # 画像タイトル設定 plt.axis("off") # 軸目盛、軸ラベルを消す plt.subplot(323) # 3行2列の3番目の領域にプロットを設定 plt.imshow(laplacian_8U_k3, cmap='gray') # ddepth=cv2.CV_8U, ksize=3の結果 plt.title('Laplacian ddepth=cv2.CV_8U, ksize=3') # 画像タイトル設定 plt.axis("off") # 軸目盛、軸ラベルを消す plt.subplot(324) # 3行2列の4番目の領域にプロットを設定 plt.imshow(laplacian_8U_k5, cmap='gray') # ddepth=cv2.CV_8U, ksize=5の結果 plt.title('Laplacian ddepth=cv2.CV_8U, ksize=5') # 画像タイトル設定 plt.axis("off") # 軸目盛、軸ラベルを消す plt.subplot(325) # 3行2列の5番目の領域にプロットを設定 plt.imshow(laplacian_32F_k3, cmap='gray') # ddepth=cv2.CV_32F, ksize=3の結果 plt.title('Laplacian ddepth=cv2.CV_32F, ksize=3') # 画像タイトル設定 plt.axis("off") # 軸目盛、軸ラベルを消す plt.subplot(326) # 3行2列の6番目の領域にプロットを設定 plt.imshow(laplacian_32F_k5, cmap='gray') # ddepth=cv2.CV_32F, ksize=5の結果 plt.title('Laplacian ddepth=cv2.CV_32F, ksize=5') # 画像タイトル設定 plt.axis("off") # 軸目盛、軸ラベルを消す plt.show()
下の図がサンプルコードの結果です。
引数のddepthとksizeを変えた場合、精度が高くカーネルサイズが大きい程、エッジの検出感度が上がっていて、変化の幅も大きい結果となりました。
- 建物の出入口の窪んで影になっている部分でも、cv2.CV_32Fでは壁の模様の横縞を検出できている。
- cv2.CV_32Fでは空の雲の形状まで検出してしまっているのため、建物だけを検出したい場合は工夫が必要。
cv2.Laplacian
関数の引数の決定のために、前処理のスムージング フィルターのカーネル サイズなどと合わせて検討することが重要かもしれません。
他のエッジ検出アルゴリズムとの比較
- ラプラシアンフィルター:「ラプラシアン フィルターとは」で示した通り、エッジが急激な部分を強調するのに優れていて、計算コストが低いです。パラメータ設定が比較的簡単ですが、感度が高いため、小さなエッジの検出が苦手です。
- ソーベルフィルター:計算量が少なく、ノイズに対する耐性があります。
- Cannyエッジ検出器:ラプラシアンよりも高度なエッジ検出アルゴリズムです。パラメータ設定が難しく、計算コストが高いですが、エッジの検出精度が高いことが特徴です。
- プリウィット フィルター:ソーベルフィルターと同様にエッジ検出に使用されます。計算コストが低く、必要な方向のエッジだけを検出することができることが大きな特徴と言えます。
おわりに
ラプラシアン フィルター(cv2.Laplacian
関数)について解説しました。
引数に設定する値を決めることが簡単ではないかもしれませんが、ラプラシアン フィルターは感度の高いエッジ検出アルゴリズムであることが確認できました。
他のエッジ検出アルゴリズムの結果と比較しながらアルゴリズムの選定を行うことで、より高品質なエッジ検出の結果を得るできると思われます。
ご質問や取り上げて欲しい内容などがありましたら、コメントをお願いします。
最後までご覧いただきありがとうございました。
参考リンク
OpenCV: Laplacian
Calculates the Laplacian of an image.
OpenCV: Laplacian
In this tutorial you will learn how to: Use the OpenCV function Laplacian() to implement a discrete analog of the Laplacian operator.
(広告)おすすめ転職エージェント
転職はあなたの可能性を広げる鍵です。その鍵を手に入れるため、おすすめ転職エージェントを紹介します。
新しいキャリアの扉を開くために、転職エージェント活用して、妥協のない転職活動を行ってください。