13 エッジ検出
産業用画像において情報が最も密集している箇所は、グレースケールが不連続になる箇所である:ワークと背景の境界、穴の輪郭、文字のストローク、キズの両側——位置決めと計測に必要なほぼすべての幾何情報は、これらのエッジ(edge)によって担われている。平坦な領域のグレースケールは照明のドリフトに伴って変動するが、エッジの位置は比較的安定している——そのためビジュアル計測ではエッジがより信頼される。チャプター 14 では「エッジのおおよその位置がわかっている状態で、探索線に沿ってそれを高精度に位置決めする」という1次元の問題を扱った。本章では、より基礎的な問題——事前にエッジの位置がわからない場合に、画像全体からすべてのエッジを抽出し、2次元のエッジマップ(edge map)を得る方法——を解説する。エッジマップは、輪郭解析、形状マッチング、および チャプター 15 で扱うハフ変換などの後続アルゴリズムの標準的な入力となる。
本章の実験は、480×360の合成シーン(図 13.1)を通して行う:グレースケール値80の背景上に、上部には高コントラストの図形——グレースケール値180(コントラスト100)の矩形と円——を配置し、下部には低コントラストの図形——グレースケール値100(コントラストわずか20)の矩形と円——を配置する。2列の図形の間には、振幅±5、周期12 pxの2次元正弦波テクスチャ(ヘアライン加工や布目などの表面テクスチャを模擬)からなる垂直なテクスチャ帯が挟まれており、画像全体に標準偏差\(\sigma=8\)のガウシアンノイズが重畳されている(再現性を確保するため、ランダムシードは固定されている)。強いエッジ、弱いエッジ、テクスチャ、ノイズが同じ場で競い合う——優れたエッジ検出器は、前者2つを報告し、後者2つを棄却すべきである。
13.1 勾配と1次オペレータ
エッジはグレースケール値がある方向に沿って急激に変化する箇所であり、「変化率」はまさに微分である。2次元画像では、水平方向と垂直方向の2つの偏微分を合成したものが勾配(gradient)\(\nabla f = (g_x, g_y)\)である:その方向はグレースケール値が最も急激に上昇する方向を指し、その大きさは変化の激しさを表す。離散画像には真の微分は存在しないため、隣接画素の差分で近似するしかない。最も広く用いられる近似はSobelオペレータ——一対の\(3\times 3\)畳み込みカーネルである:
\[ h_x = \begin{bmatrix} -1 & 0 & 1\\ -2 & 0 & 2\\ -1 & 0 & 1 \end{bmatrix}, \qquad h_y = \begin{bmatrix} -1 & -2 & -1\\ 0 & 0 & 0\\ 1 & 2 & 1 \end{bmatrix}. \]
\(h_x\)は水平方向に中心差分を行い、垂直方向に\([1\ 2\ 1]\)の重み付き平滑化を行う;\(h_y\)はその正反対である。差分と平滑化は互いに垂直な方向に実行される——微分そのものがノイズを増幅するため、微分に対して垂直な方向に事前に平均化することは、チャプター 6 の手法を用いて差分に「保険をかける」方法である。
Sobelカーネルは分離可能である:\(h_x = [1\ 2\ 1]^{\mathsf T} \otimes [-1\ 0\ 1]\)、すなわち「垂直方向の平滑化 × 水平方向の差分」である。平滑化部分を\([1\ 1\ 1]\)に置き換えるとPrewittオペレータとなり、\([3\ 10\ 3]\)に置き換えると回転対称性に優れたScharrオペレータとなる——骨格はいずれも「一方向を平滑化し、もう一方向を差分する」である。
2つの成分から、勾配の振幅(magnitude)と方向(direction)が得られる:
\[ A = \sqrt{g_x^2 + g_y^2}, \qquad \theta = \operatorname{atan2}(g_y,\, g_x). \]
振幅は「エッジらしさ」を表し、方向は「エッジの向き」を表し(勾配方向はエッジの走向と垂直である)、Cannyパイプラインでは両方が用いられる。
まず、成分の方向選択性を見てみよう。図 13.2 は、テストシーンに\(3\times 3\)のSobelを適用した2つの符号付き成分である(表示時にオフセット128、スケール1/4を適用:中間グレーより明るいものが正、暗いものが負)。\(g_x\)は垂直エッジのみを点灯させる:矩形の左縁は正(80から180へ遷移しグレースケール値が上昇するため)、右縁は負であり;円の輪郭は左が明るく右が暗い——同じ円でも、両側の勾配方向は逆になる;矩形の上下縁は\(g_x\)では完全に消失する。\(g_y\)の挙動はちょうど転置されたものとなる:水平エッジのみが見え、上縁が正、下縁が負である。低コントラスト図形の応答は高コントラスト図形のわずか1/5であり、両方の画像で非常に暗く見える——エッジ応答はコントラストに比例する、というこの単純な事実が、今後すべての閾値処理問題の根源となる。
2つの成分を合成して振幅を求めると、方向情報が消失しすべてのエッジが同じように明るくなり、図 13.3 が得られる——これはSDKのSobelAmplitudeの直接出力である。強い輪郭は鮮明で明るく、弱い輪郭はかろうじて識別できるが、背景にはノイズによる粒子状の応答が敷き詰められている:微分がノイズを増幅したため、1次オペレータだけでは「エッジの可能性」を示すグレースケールマップが得られるに過ぎず、クリーンなエッジマップにはまだ決定的な工程がいくつか不足している。
13.2 2次オペレータ:Laplace
もう一つのアプローチは2次微分である。ラプラシアン(Laplacian)は、2方向の2次微分の和\(\nabla^2 f = \partial^2 f/\partial x^2 + \partial^2 f/\partial y^2\)であり、8近傍の離散カーネルは
\[ h = \begin{bmatrix} 1 & 1 & 1\\ 1 & -8 & 1\\ 1 & 1 & 1 \end{bmatrix}. \]
その特長はゼロ交差(zero crossing)による位置決めである:階段状のエッジを横切るとき、1次微分は単一のピークとなるのに対し、2次微分はピークの両側にそれぞれ極値を持ち、エッジの位置で正確にゼロを通過する——理論上、ゼロ交差点を検出することで、方向を選択することなく、自然に閉じた単一画素幅のエッジ輪郭が得られるはずである。
しかし、実測ではこの考えに冷水が注がれる。図 13.4 は、SDKのLaplace(\(3\times 3\)、8近傍カーネル、絶対値出力)をテストシーンに適用した応答である:画像全体がノイズの粒子に埋もれ、強い輪郭はかろうじて見える二重線として残っているに過ぎない——2次微分はエッジの両側にそれぞれ極値を持つため、絶対値表示では間に挟まれた真のゼロ交差位置を挟んで平行な2本の応答線となる。弱い輪郭は完全にノイズに沈んでいる。2つの要因が重なっている:第一に、2次差分は高周波ノイズを1次差分よりもはるかに強く増幅するため、\(\sigma=8\)のノイズがこれを通ると有効信号が消え失せる;第二に、ラプラシアンは方向情報を提供しないため、後続の処理で勾配のようにエッジ法線方向に沿って精密に処理することができない。これが産業実務において1次オペレータが絶対的に支配的である理由である——2次オペレータの理論的な優雅さは、ノイズに対する脆弱さには敵わない。
2次オペレータを実用化するための古典的な救済策はLoG(Laplacian of Gaussian)である:まずガウシアン平滑化を行い、その後ラプラシアンを求めることは、「メキシカンハット」形のカーネルで1回畳み込みを行うことと等価である;その差分近似であるDoG(異なる\(\sigma\)を持つ2つのガウシアンの差)は、現在でもブロブ検出やSIFT特徴量で活躍している。
13.3 Cannyパイプライン
1次オペレータの出力を、クリーンで単一画素幅の連続したエッジマップに変換するための公認の標準的な答えは、Cannyアルゴリズム(Canny 1986)である。Cannyは「優れたエッジ検出」を3つの基準——良好な検出(漏れも虚報もない)、良好な位置決め、単一の応答(1本のエッジは1回だけ報告される)——として定式化し、準最適な実装として4段階のパイプラインを導き出した。
第1段階:ガウシアン平滑化。 微分はノイズを増幅するため、まずガウシアンフィルタ(本例では\(3\times 3\)、\(\sigma=0.8\))でノイズを抑える。\(\sigma\)は検出スケールのつまみである:大きいほど耐ノイズ性は高まるが、隣接するエッジがぼやけて融合し、位置決め精度も低下する。
第2段階:勾配計算。 平滑化後の画像にSobelを適用し、振幅\(A\)と方向\(\theta\)を求める(図 13.5 (a))。このとき、強い輪郭は数画素幅の明るい帯状になっている——平滑化によって階段が広げられ、勾配のピークもそれに伴って広がったためである。
第3段階:非極大値抑制(non-maximum suppression、NMS)。 勾配振幅はエッジに垂直な方向に尾根状に分布しており、真のエッジは尾根線上にのみ存在すべきである。NMSは以下のように動作する:各画素の勾配方向を4つのセクタ(水平、45°、垂直、135°)に量子化し、勾配方向に沿って両側の隣接画素と振幅を比較し、中心が片側以上かつもう片側より厳密に大きい場合にのみ中心を保持し、そうでなければゼロにする。比較の非対称性(片側は\(\ge\)、もう片側は\(>\))は意図的なものである:尾根の頂上に平坦な台地が現れた場合に片側だけを保持し、細線化結果が単一画素になることを保証する。図 13.5 (b) では、数画素幅の明るい帯が細い尾根に削り取られている——ただし、ノイズの局所極大も同様に生き残っている。NMSは「細さ」を処理するだけで、「真実性」を処理するわけではない。
第4段階:二重閾値によるヒステリシス連結(hysteresis thresholding)。 単一の閾値でエッジとノイズを分離すると、どちらか一方を犠牲にせざるを得なくなるため、Cannyは2つの閾値を用いる:振幅 \(\ge\) 高閾値の画素はシード——十分に強く、エッジであると確信できる;振幅が低閾値と高閾値の間にある画素は候補——それ自体だけでは根拠が不十分だが、シードと8近傍で連結していればエッジの延長として受け入れられる。実装では、すべてのシードから領域成長を行い、振幅 \(\ge\) 低閾値のすべての連結画素に拡張する。直感的には明らかである:強いエッジに接している弱い応答は、同じエッジのコントラストが弱まった部分である可能性が高く;孤立した弱い応答はノイズである可能性が高い。強いものが保証し、弱いものがそれに従う——これによってのみ、コントラストが強くなったり弱くなったりする輪郭を完全に連続した状態で保存することができる。
最終結果である@fig-ed-canny-steps-c には、合計2163個のエッジ画素が含まれる:4つの図形の輪郭はすべて保存され、単一画素幅でほぼ完全に連続しており、低コントラストの矩形と円も完全に閉じている。対照として、NMSとヒステリシス連結を省略し、勾配振幅に対して直接単一閾値による二値化(閾値は同じ60)を行った結果が@fig-ed-sobel-thresh である:4064個のエッジ画素があり、約2倍になっている。増えた画素はすべて無駄である——強い輪郭は2~4 pxの太い帯になり(勾配のピークには幅があり、単一の閾値はピーク全体を切り取ってしまうため)、弱い輪郭は途切れ途切れの破線に砕けている(振幅が閾値の前後で変動するため、閾値を超えた部分は残り、超えなかった部分は途切れる)。太さが不均一で途切れ途切れのエッジマップは、後続の輪郭追跡や幾何フィッティングにとって悪い入力である;NMSが「太さ」を解消し、ヒステリシス連結が「途切れ」を解消する——これがまさにCannyの最後の2段階それぞれの価値である。
13.4 閾値の工学的選択
Canny は閾値を1つから2つに変更したため、選択の責任も倍増した。両方の失敗方向を目で確認する価値がある(図 13.7)。
閾値のペアを(8, 24)に下げると 図 13.7 (a) が得られる:64077 個のエッジ画素——これは正しい結果の30倍である。ノイズの勾配応答が低閾値を広範囲で超え、さらにヒステリシス連結によって互いに結びつくことで、画像全体が密な網目状になる。中央のテクスチャ帯の正弦波格子もすべて出現する。ここで明らかにすべき設計上の詳細がある:本シーンのテクスチャは振幅が ±5、周期が 12 px であり、その勾配幅值のピークは実測で約19となり、デフォルトの低閾値20のすぐ下に収まっている。そのため(20, 60)の結果ではテクスチャ領域はきれいなままだが、(8, 24)ではノイズとともにテクスチャが出現する。実際の生産ラインにおけるヘアライン加工、ブラスト処理、布目の表面は、いずれもこのテクスチャ帯の役割を果たす。それらが「エッジ」かどうかは、閾値をどこに設定するかに完全に依存する。
逆方向に閾値のペアを(40, 120)に上げると 図 13.7 (b) が得られる:残ったエッジ画素は 918 個だけで、画面は極めてきれいになる——しかし、コントラストの低い矩形と円が全体として消失している。コントラスト20のエッジは平滑化後の勾配幅值が120に満たず、シード(種)が1つも生成されないため、ヒステリシス連結は素材のない状態ではどうにもならない。検出タスクにおいて、見落としは通常、誤検出よりも致命的である。誤検出は後続のスクリーニングで救済できるが、見落とした対象は二度と戻ってこない。
工学的な選択法則は2段階にまとめられる。第1段階:高閾値を定める——ノイズの勾配レベルの3~4倍とする。 画像の空白領域で勾配幅值の代表的なレベル(例えば平均値に標準偏差の3倍を加えた値、または高位分位数)を計測し、高閾値をその3~4倍に固定する。これにより、ノイズからシードが生成される可能性はほぼなくなる。第2段階:低閾値を定める——高閾値の1/2から1/3を起点とする。 低閾値は輪郭の連結性を司る。高閾値に近すぎるとヒステリシス連結は有名無実になり、低すぎるとノイズが橋渡しをしてしまう。本章の(20, 60)はまさにこの法則に従って選ばれた。\(\sigma=8\) のノイズは平滑化後の勾配幅值がおおむね15程度であり、高閾値60はその約4倍、低閾値は高閾値の1/3となっている。最後に実画像を用いてオンラインで微調整する——法則が与えるのは出発点であり、終点ではない。
これは チャプター 7 のヒストグラムによる閾値選択と思想的に同根である。前者はグレースケール値の分布に対して閾値を設定して前景と背景を分離していたのに対し、ここでは勾配幅值の分布に対して閾値を設定してエッジとノイズを分離している。対象は変わったが、「分布を見て、境界を見つける」という方法論は変わっていない。
13.5 サブピクセルと展望
Canny が出力するエッジ画素の座標は整数値のみであり、位置決め精度は1 pxに固定される。識別と計数にはこれで十分だが、計測には全く不十分である——10 μm/px のシステムでは、1 pxの量子化誤差は10 μmに相当する。幸い、補修のコストは低い。NMS は勾配方向に沿った3つの幅值 \(g_{-1}, g_0, g_{+1}\) を比較するため、これらに放物線補間を行うことで、稜線の頂点位置をサブピクセル(subpixel)精度で特定できる——これはまさに チャプター 14 で1次元プロファイルに対して用いたのと同じ頂点公式である。輪郭に沿って点ごとに補間することで、画素列はサブピクセル輪郭にアップグレードされる。工業的な実務では、局所的なエッジや穴のみを計測する必要がある場合は、チャプター 20 のキャリパーツールを用いて ROI 内で直接1次元サブピクセル位置決めを行うことが一般的である。全画像のエッジ検出は、対象の位置が事前にわからない場合に用いられる。
SciVision SDK には Canny と相補的なサブピクセル抽出器として SciSvExtractLinesGauss も用意されている。これは Steger 式の曲線構造検出を実装している (Steger 1998)。注意すべきは、これが階段状エッジではなく線——幅のある稜線または谷線(例えば傷、ボンディングワイヤ、接着剤の軌跡)を探す点である。ExtractLinesGauss はガウス微分に基づき、サブピクセル精度の中心線輪郭配列を直接出力し、各線に方向 angle、応答強度 response、左右の半値幅、非対称度、コントラストといった属性を付加する。パラメータには見慣れた特徴がある:sigma は検出スケール、lowThresh/highThresh はヒステリシス二重閾値——Canny と同じ思想である。lineModel はガウス線、棒状線、放物線の3種類のプロファイルモデルから選択する。付属の CalculateParameter を用いれば、想定される線幅とコントラストから sigma と二重閾値を逆算できるため、手動での調整が不要になる。本章ではこれに関する実験は行っておらず、単なる指針として提示する。対象が「グレースケール値の段差」ではなく「幅のある線」である場合は、このツールを思い出すべきである。
13.6 SciVision 実装
本章で使用した演算子は SCIMV::SciSvFilter によって提供される:
SCIMV::SciSvFilter f;
SciImage amp, lap, smooth;
SciROI roi;
SciPoint tl(0, 0), br(W, H); // GenRect1 の右下は排他端点:全画像を指定するには (W, H) を渡す
roi.GenRect1(tl, br);
// Sobel 勾配幅值:sobelType=SUM_SQRT(1) は sqrt(gx^2+gy^2)、kernelSize=3(ガウス事前平滑化カーネルのサイズ)
long rc = f.SobelAmplitude(img, roi, &, SCIMV::SUM_SQRT, 3);
// Laplace:3x3 カーネル、filterMask=1(8近傍カーネル)、resultType=0(絶対値出力)
rc = f.Laplace(img, roi, &lap, 3, 1, 0);
// Canny 事前平滑化:3x3 ガウス、sigma=0.8
rc = f.Gaussian(img, roi, &smooth, 3, 3, 0.8, 0.8);SobelAmplitude の sobelType は幅值の合成方法を選択し、SUM_SQRT は標準的な二乗和の平方根である。kernelSize=3 は内部ガウス事前平滑化カーネルのサイズ(有効範囲 [1,11])であり、Sobel 微分カーネルのサイズとは無関係である。Laplace の filterMask=1 は8近傍カーネル(中心が \(-8\))を選択し、resultType=0 は絶対値を出力する——図 13.4 の二重応答はまさにこれに起因する。
SDK にはワンステップで実行できる Canny(srcImage, ROI, dstImage) も存在する——しかし、これには閾値パラメータが一切なく、高低閾値はすべて内蔵されているため、シーンごとに調整することができず、当然ながら セクション 13.4 の感度実験を行うこともできない。そのため本章では NMS とヒステリシス連結を手書きした(完全なコードはサンプルプロジェクトを参照)——これはまさに自分の手で一度書く価値のある部分である。生産ラインのコードで制御可能な閾値を持つ Canny が必要な場合も、同様にこの2段階を自作することを推奨する。勾配と平滑化は SDK に任せ、決定ロジックは自分の手元に置いておくべきである。
また、正直に記録しておかなければならない落とし穴がある。現行バージョンの SciSvFilter::Gaussian は、どのような ROI を渡しても出力画像の右端1~2列を破損させる(カーネルサイズ3の場合、最後の2列がゼロになる)。ゼロになった列は隣接する列との間にコントラスト80以上の偽の段差を形成し、エッジ画像では画像の高さ方向全体にわたる偽のエッジとして現れる。サンプルプロジェクトの対策は、最後の有効列を右方向にコピーして補修することである:
// SDK の不具合への対策:Gaussian が出力の右端2列をゼロにするため、最後の有効列をコピーして埋める
for (int r = 0; r < H; ++r) {
smooth[r * W + W - 2] = smooth[r * W + W - 3];
smooth[r * W + W - 1] = smooth[r * W + W - 3];
}本章のすべての画像と統計値を生成する完全な実行可能プロジェクトは code/edge_detection/ に配置されている。すべてのエッジ画素数(4064 / 2163 / 64077 / 918)はプログラムの実際の出力である。
工業事例:ガラス端辺のカケ検出における閾値調整
あるガラス深加工ラインでは、Canny を用いて端辺の輪郭を抽出した後、輪郭の局所的な欠落を検査してカケを判定していた。高低閾値は設置時の画像に合わせて調整された後、固定されていた。数ヶ月後、夜勤帯の見落とし率が上昇した。LED の光度低下に夜間の環境光の低下が重なって画像のコントラストが低下し、微小で淡いカケの勾配幅值が固定された高閾値を下回り、シードが生成されなくなったためである。改造では、シフトごとに自動調整を行うようにした。各シフトの開始時に、端辺の隣の空白領域でノイズの勾配レベル(勾配幅值の高位分位数)を推定し、高閾値 \(=K\times\) その推定値(\(K\approx 3.5\))、低閾値を高閾値の半分に設定する。その後、ランプの経年劣化や交換が数回行われたが、見落とし率は安定したままであった。教訓:勾配閾値は「信号がノイズよりどれだけ強いか」を測るものであり、固定された絶対値は照明の劣化とともに静かに無効化される——閾値はノイズに固定すべきであり、ある日の良好な照明に固定すべきではない。
13.7 まとめ
- エッジはグレースケール値の不連続点であり、勾配によって特徴づけられる:Sobel は「一方向で差分を取り、垂直方向で平滑化する」一対の \(3\times 3\) カーネルで勾配を近似し、幅值が「強さ」を、方向が「向き」を決める。エッジ応答はコントラストに比例する——これがすべての閾値問題の根源である。
- 2階演算子は理論的に優雅だが、実務的には脆弱である:\(\sigma=8\) のノイズ下では、Laplace のゼロクロス位置決めは画面いっぱいの粒状ノイズと二重線応答に退化し、方向情報も提供しない——工業用エッジ検出は1階演算子が支配的である。
- Canny の4段階はそれぞれ役割を持つ:ガウス平滑化はノイズに対抗し、勾配は「可能性」を計算し、NMS は応答帯を単一画素の細い稜線に削り、ヒステリシス閾値は「強いものが保証し、弱いものが従う」ことでコントラストが変動する連結輪郭を保存する。実験では、同じ閾値での単一閾値二値化が4064 pxの粗く途切れたエッジを出力したのに対し、Canny は2163 pxの単一画素連結輪郭を出力した。
- 閾値をノイズに固定する:高閾値をノイズの勾配レベルの3~4倍に設定し、低閾値を高閾値の1/2~1/3から始め、オンラインで微調整する。本章の実験では、(8, 24)はノイズとテクスチャが結果を埋没させ(64077 px)、(40, 120)は弱い輪郭をすべて消し去った(918 px)。
- 計測にはサブピクセルが必要である:NMS の3点の幅值に放物線補間を行うことでサブピクセルエッジが得られる。線状構造(傷、ボンディングワイヤ)には、Steger 式の
ExtractLinesGaussを用いてサブピクセル中心線と幅、コントラストなどの属性を直接抽出できる。
エッジ検出の2つの基礎文献は原典に立ち返る価値がある。Canny が最適性基準から本章の4段階手法を導出した古典的な論文 (Canny 1986)、および Marr と Hildreth が2階ゼロクロス検出(LoG)の理論的基礎を築いた研究 (Marr と Hildreth 1980) である。エッジと線のサブピクセル抽出、検出スケールの選択、およびそれらの精度分析に関する体系的な解説については、Steger らの著書をさらに参照できる (Steger, Ulrich, と Wiedemann 2018)。










