OpenCV

[OpenCV] エッジ検出を使って輪郭抽出してみよう!

今回は、画像のエッジを検出するための様々な方法を分かりやすく解説していきます!

本記事では以下のことを解説しています。

  • 転職のためのスキルアップとして効率的に学びたい
  • 副業としてプログラミングができるようになりたい
  • 独学での勉強に限界を感じている

これらに該当する方はプログラミングスクールがスキルアップの近道です!
是非参考にしてみてください。

【2023年最新】プログラミング上達の近道とは?スクールに通うメリットやおすすめランキングも徹底解説!プログラミングを上達するための近道を紹介しています。また、2023年度に人気の優良プログラミングスクールをランキング形式で紹介しています!特にプログラミングスクールの選び方を難易度、料金、特徴など様々な観点から紹介しています。自分自身の目的に合ったスクール選びの助けとなれば幸いです。...

未経験でも安心!
学生30代の方にもおすすめなプログラミングスクールがあります

エッジ検出とは

エッジ検出は、画像上の物体を検出しその輪郭を抽出する手法です。

edge-detection1

Python OpenCVを用いてエッジ検出をする方法は以下の3つが挙げられます。それぞれエッジの検出方法が異なります。

  • ラプラシアン処理
  • Sobel処理
  • Canny処理

1つずつ分かりやすく使い方を説明していきたいと思います!

ラプラシアン処理

まずはラプラシアン処理を用いてエッジ検出をする方法についてです。本記事では、理論的な部分は割愛しますが、二次微分(差分)系のフィルタになります。

ラプラシアン処理を行うためにはcv2.Laplacian()を使います。実際のコードは以下の通りです。

import cv2

def main():
    image=cv2.imread("C:\Imagefile\egao.png")

    edge_im=cv2.Laplacian(image,-1)

    cv2.imshow("edge filter",edge_im)

    cv2.waitKey()
    cv2.destroyAllWindows()
if __name__=="__main__":
    main()

(実行結果)

edge-detection-image1

ここでcv2.Laplacian(image,-1)第二引数では、出力画像のデータ型を指定します。一覧を以下の表に示します。

第二引数
-1原画像の配列を使用
cv2.CV_8Sint 8
cv2.CV_8Uunit 8
cv2.CV_16Sint 16
cv2.CV_16Uunit 16
cv2.CV_32Sint 32
cv2.CV_32Ffloat 32
cv2.CV_64Ffloat 64

基本的には、”-1” を使用すると良いと思います。
データ型が正しく指定されていないとこのように正しくエッジが検出されませんでした

edge-detection-image2

また、第三引数にカーネルサイズを指定することもできますがラプラシアン処理では変化が見られませんでした。カーネルサイズを指定する場合は、int型の数値第三引数として入力します。

edge-detection-image3

他方で、原画像のままラプラシアン処理を行うと、輝度に偏りが出ているため上手く検出されない場合があります。

そのような場合には、輝度平滑化処理を原画像に施してからエッジ検出を行うことで精度よく検出できるようになります。

輝度平滑化を行う場合は以下のコードを先に処理させます。ただし、輝度平滑化処理は、グレイスケール画像にのみ適用できる点に注意してください。

#クレイスケールで画像読み込み
  image=cv2.imread("C:\Imagefile\egao.png",cv2.IMREAD_GRAYSCALE)
#輝度の平滑化
    dst=cv2.equalizeHist(image)

(輝度平滑化後の実行結果)

edge-detection-image4

Sobel処理

続いてSobel処理です。Sobel処理は、ガウシアン処理を用いて平滑化を行いつつエッジ検出をするものです。平滑化を行っているためノイズの影響を低減しつつエッジ検出を行うことができるという特徴があります。

参考:[フィルタ処理] 平滑化の方法について

import cv2

def main():
    image=cv2.imread("C:\Imagefile\egao.png")

    edge_im=cv2.Sobel(image,-1,0,1)

    cv2.imshow("edge filter",edge_im)

    cv2.waitKey()
    cv2.destroyAllWindows()
if __name__=="__main__":
    main()

(実行結果)

edge-detection-image5

ここで、cv2.Sobel()はこのように記述しています。

 edge_im=cv2.Sobel(image, bit, dx, dy, ksize)

第一引数
処理対象の画像を指定

第二引数
出力配列のデータ型を指定(基本は-1を指定)
(詳細はラプラシアン処理項目で紹介しています)

第三引数
x方向の微分次数を指定

第四引数
y方向の微分次数を指定

第五引数
カーネルサイズを指定(1,3,5,7のどれかを指定)

これらの引数を踏まえて、色々と値を変化させてみた結果を以下に示していきたいと思います!

まず、y方向の微分次数を「1」として、x方向の微分次数を「0」とした場合のエッジ検出結果はx方向を微分する場合と少し異なったエッジが検出されました。

xとyの両方向の微分次数を「1」とした場合には、エッジの強さが弱くなってしまいました。

ksizeのみを変更させた場合には、あまり変化は見られませんでした。

edge-detection-image6

Canny処理

Canny処理は、画像中の強いエッジを検出し弱いエッジ(少数の画素で構成されたエッジ)は削除することができるエッジ検出手法です。

特徴
  • エッジの輪郭を分かりやすく検出できる
  • 誤検出が少ない

実際にどのように検出されるか見た方が早いと思いますので以下にコードを示します。

import cv2

def main():
    image=cv2.imread("C:\Imagefile\egao.png")

    edge_im=cv2.Canny(image,40.0,200.0)
    cv2.imshow("edge filter",edge_im)

    cv2.waitKey()
    cv2.destroyAllWindows()
if __name__=="__main__":
    main()

(実行結果)

edge-detection-image7

このように検出できました。

第一引数
処理対象の画像を指定

第二引数
第1しきい値(最小しきい値)をfloat型で指定

第三引数
第2しきい値(最大しきい値)をfloat型で指定

最小しきい値最大しきい値を任意に変化させてエッジ検出した結果を以下に示します。最小しきい値が小さすぎる場合にはエッジが誤検出されています。逆に最小しきい値が大きすぎる場合にはエッジが正しく検出されませんでした。

この二つの変数を正しく調整すると、(65.0, 200.0)の結果のようにエッジが正しく検出されるようになりました!

edge-detection-image8

エッジ検出したい画像にもよると思いますが、今回用いたような単純な画像ではCanny処理が最も正確に輪郭検出できていることが分かりました。

もっとOpenCVやPythonについて学びたい人はコチラの記事を参考にしてみて下さい!

[2024年版][難易度別] Python学習にオススメな参考書:厳選9冊!Pythonを勉強する初心者から中級者を対象として、2023年現在で本当に良いと感じた厳選9冊を紹介しています。Pythonの基礎を学べる書籍から、GUI、アプリ作成、機械学習などを学べる書籍まで幅広く紹介しています!レビューも書き込んでいます!...

また、プログラミングをしていて疲れやすいなと感じる場合にはこちらを参考にしてみて下さい!

プログラミング学習に役立つガジェット厳選4選!筆者が実際に使ってみて有用だと感じた、プログラミングに役立つガジェットを紹介しています!プログラミングされている方には是非とも読んでみてほしい記事です!...

以上となります。最後まで見ていただきありがとうございました!