Pynote

Python、機械学習、画像処理について

OpenCV - Canny 法で画像からエッジを検出する。

概要

OpenCV でCanny 法で画像からエッジを検出する方法について紹介する。

Canny エッジ検出

Canny エッジ検出は以下の複数の処理を行い、画像からエッジを検出する処理である。

1. 水平方向、垂直方向のソーベルフィルタを適用し、輝度勾配及び勾配強度を計算する。
2. Non Maximum Suppression で細いエッジを抑制する。
3. 2つの閾値を使用して、ヒステリシス処理を行い、誤検出を抑制する。

より詳しい説明は以下のリンクを参照

Canny Edge Detection Step by Step in Python
OpenCV のソースコード

OpenCV

edges = cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]])
  • image: 入力画像 (1チャンネル)
  • threshold1: ヒステリシス処理の際の1つ目の閾値
  • threshold2: ヒステリシス処理の際の2つ目の閾値
  • edges: 出力画像 (引数経由で受け取る場合は指定する。)
  • apertureSize: ソーベルフィルタのフィルターサイズ
  • L2gradient: true の場合は勾配強度のノルムに L2 ノルムを使用する。false の場合は L1 ノルムを使用する。

サンプルコード

入力画像

import cv2

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

# Canny エッジ検出を行う。
edges = cv2.Canny(img, 100, 400)

# 結果を保存する。
cv2.imwrite("edges.png", edges)

出力画像

パラメータ調整について

エッジ検出の結果が芳しくない場合は以下の4つのパラメータを調整するとよい。
試した感じでは、apertureSize=3, L2gradient=True が基本的には良さそう。
threshold1、threshold2 は画像によって異なってくるので調整が必要である。

  • threshold1: ヒステリシス処理の際の1つ目の閾値
  • threshold2: ヒステリシス処理の際の2つ目の閾値
  • apertureSize: ソーベルフィルタのフィルターサイズ
  • L2gradient: true の場合は勾配強度のノルムに L2 ノルムを使用する。false の場合は L1 ノルムを使用する。

ipywidget

パラメータを調整するための ipywidget を使用したサンプルコード

import cv2
from IPython.display import Image, display
from ipywidgets import widgets


def imshow(img):
    """画像を Notebook 上に表示する。
    """
    ret, encoded = cv2.imencode(".png", img)
    display(Image(encoded))


def canny(img, thresh, apertureSize, L2gradient):
    """2値化処理を行い、結果を表示する。
    """
    thresh1, thresh2 = thresh
    edges = cv2.Canny(
        img, thresh1, thresh2, apertureSize=apertureSize, L2gradient=L2gradient
    )
    imshow(edges)


# パラメータ「threshold1」「threshold2」を設定するスライダー
thresh_slider = widgets.SelectionRangeSlider(
    options=np.arange(1000), index=(100, 200), description=f"threshold"
)
thresh_slider.layout.width = "400px"

# パラメータ「apertureSize」を設定するスライダー
aperture_size_slider = slider = widgets.IntSlider(
    min=3, max=7, step=2, value=3, description="apertureSize: "
)
aperture_size_slider.layout.width = "400px"

# パラメータ「L2gradient」を設定するチェックボックス
l2_gradient_checkbox = widgets.Checkbox(value=False, description="L2gradient: ")
l2_gradient_checkbox.layout.width = "400px"

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

# ウィジェットを表示する。
widgets.interactive(
    canny,
    img=widgets.fixed(img),
    thresh=thresh_slider,
    apertureSize=aperture_size_slider,
    L2gradient=l2_gradient_checkbox,
)