概要
エッジ検出は私たちが自然に行うことですが、コンピューターのルールを定義することに関しては簡単ではありません。 さまざまな方法が考案されてきましたが、1986 年に John F. Canny によって開発された方法が現在の方法であり、適切に Canny メソッドと名付けられています。
高速で、かなり堅牢で、このタイプの手法で機能する最高の機能を備えています。 このガイドの終わりまでに、ビデオでリアルタイムのエッジ検出を実行し、次のようなものを作成する方法を理解できます。
キャニーエッジ検出
キャニー法とは? これは、次の XNUMX つの異なる操作で構成されます。
- ガウス平滑化
- 勾配の計算
- ノンマックスサプレッション
- ヒステリシスしきい値
ガウス平滑化 入力画像を「修正」し、ノイズを和らげて最終出力をよりきれいにするための最初のステップとして使用されます。
画像のグラデーション エッジ検出のために以前のアプリケーションで使用されていました。 最も顕著なのは、Sobel フィルターと Scharr フィルターが画像のグラデーションに依存していることです。 Sobel フィルターは XNUMX つのカーネルに要約されます (Gx & Gy)、ここで Gx 水平方向の変化を検出し、 Gy 垂直方向の変化を検出します:
G
x
=
[
-
1
0
+
1
-
2
0
+
2
-
1
0
+
1
]
G
y
=
[
-
1
-
2
-
1
0
0
0
+
1
+
2
+
1
]
それらを画像の上にスライドさせると、それぞれの向きで線を「ピックアップ」(強調) します。 Scharr カーネルは、異なる値で同じように機能します。
G
x
=
[
+
3
0
-
3
+
10
0
-
10
+
3
0
-
3
]
G
y
=
[
+
3
+
10
+
3
0
0
0
-
3
-
10
-
3
]
これらのフィルターは、画像を畳み込むと、特徴マップを生成します。
画像クレジット: Davidwkennedy
これらの機能マップについて、次を計算できます。 勾配の大きさ & グラデーションの向き – つまり、変化の激しさ (何かがエッジである可能性) と、変化がどの方向を指しているか。 Gy は垂直方向の変化 (Y 勾配) を表し、Gx は水平方向の変化 (X 勾配) を表すため、ピタゴラスの定理を適用するだけで大きさを計算できます。 「正しい」方向:
$$
{G} ={sqrt {{{G} _{x}}^{2}+{{G} _{y}}^{2}}}
$$
大きさと向きを使用して、エッジが強調表示された画像を作成できます。
画像クレジット: Davidwkennedy
ただし、レンガのテクスチャーからも、どれだけのノイズがキャッチされたかがわかります。 画像のグラデーションはノイズに非常に敏感です。 これが、Sobel フィルターと Scharr フィルターがコンポーネントとして使用された理由ですが、Canny の方法の唯一のアプローチではありません。 ここでもガウス平滑化が役立ちます。
ノンマックスサプレッション
ソーベル フィルターの顕著な問題は、エッジがはっきりしないことです。 誰かが鉛筆で線を引いて画像の線画を作成したわけではありません。 光が徐々に拡散するため、通常、画像のエッジはそれほど明確ではありません。 ただし、エッジに共通の線を見つけて、その周りの残りのピクセルを抑制し、代わりにきれいで細い分離線を生成できます。 これはノンマックスサプレッションとして知られています! 非最大ピクセル (3×3 カーネルなどの小さなローカル フィールドで比較するピクセルよりも小さいピクセル) は抑制されます。 このコンセプトはこれ以外のタスクにも適用できますが、ここではこのコンテキストにバインドしましょう。
ヒステリシスしきい値
多くの非エッジは、照明条件、画像内の素材などにより、エッジとして評価される可能性が高く、おそらく評価されるでしょう。これらの誤算が発生するさまざまな理由により、エッジが確実に何であり、何であるかを自動的に評価することは困難です。だ。 「本物の」エッジが「偽の」エッジよりも強いと仮定して、勾配のしきい値を設定し、より強いものだけを含めることができます。
しきい値処理は通常とほぼ同じように機能します。勾配が下限しきい値を下回っている場合は削除 (ゼロ アウト) し、指定された上限しきい値を上回っている場合は保持します。 下限と上限の間にあるものはすべて「グレーゾーン」にあります。 しきい値の間のいずれかのエッジが接続されている場合 決定的なエッジ (しきい値を超えるもの) – それらもエッジと見なされます。 それらが接続されていない場合、それらは誤って計算されたエッジの犠牲者である可能性があります.
それがヒステリシス閾値です! 実際には、偽エッジとして分類するものに応じて、最終出力をクリーンアップし、偽エッジを削除するのに役立ちます。 適切なしきい値を見つけるには、通常、しきい値の下限と上限を変えて実験するか、Otsu 法や Triangle 法などの自動化された方法を使用します。
画像をロードしてグレースケールしてみましょう (Canny、ちょうど Sobel/Scharr が画像をグレースケールにする必要があるように):
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('finger.jpg', cv2.IMREAD_GRAYSCALE)
img_blur = cv2.GaussianBlur(img, (3,3), 0)
plt.imshow(img_blur, cmap='gray')
指のクローズ アップ画像は、エッジ検出の良いテスト グラウンドとして機能します。画像から指紋を識別するのは簡単ではありませんが、指紋を近似することはできます。
cv2.Canny() による画像のエッジ検出
Canny のアルゴリズムは、OpenCV を使用して適用できます。 Canny()
方法:
cv2.Canny(input_img, lower_bound, upper_bound)
ベストプラクティス、業界で認められた標準、および含まれているチートシートを含む、Gitを学習するための実践的で実用的なガイドを確認してください。 グーグルGitコマンドを停止し、実際に 学ぶ それ!
下限と上限の間の適切なバランスを見つけるのは難しい場合があります。 両方が低い場合、エッジはほとんどありません。 下限が低く、上限が高い場合、ノイズが発生します。 両方が高く、互いに近い場合、エッジはほとんどありません。 適切なスポットは、境界間に十分なギャップがあり、適切なスケールにあります。 実験!
入力画像はキャニー法によってぼかされますが、多くの場合、ぼかすことでメリットが得られます それも入ります。 このメソッドは、残りの操作を実行する前に入力に 5×5 ガウスぼかしを適用しますが、このぼかしを使用してもノイズが浸透する可能性があるため、アルゴリズムにフィードする前に画像をぼかしました。
edge = cv2.Canny(img_blur, 20, 30)
fig, ax = plt.subplots(1, 2, figsize=(18, 6), dpi=150)
ax[0].imshow(img, cmap='gray')
ax[1].imshow(edge, cmap='gray')
この結果:
の値 20
& 30
ここでは恣意的ではありません。さまざまなパラメーターでメソッドをテストし、適切な結果が得られると思われるセットを選択しました。 これを自動化できますか?
cv2.Canny() の自動しきい値処理?
最適なしきい値のセットを見つけることができますか? はい。ただし、常に機能するとは限りません。 適切な値を独自に計算してから、 sigma
そのしきい値の周り:
lower_bound = (1-sigma)*threshold
upper_bound = (1+sigma)*threshold
日時 sigma
、つまり、 0.33
– 境界は 0.66*threshold
& 1.33*threshold
、その周りに〜1/3の範囲を許可します。 しかし、 threshold
より難しいことです。 OpenCV は、Otsu のメソッド (バイモーダル イメージに最適) と Triangle メソッドを提供します。 XNUMX 番目のオプションとしてピクセル値の単純な中央値を取得するだけでなく、両方を試してみましょう。
otsu_thresh, _ = cv2.threshold(img_blur, 0, 255, cv2.THRESH_OTSU)
triangle_thresh, _ = cv2.threshold(img_blur, 0, 255, cv2.THRESH_TRIANGLE)
manual_thresh = np.median(img_blur)
def get_range(threshold, sigma=0.33):
return (1-sigma) * threshold, (1+sigma) * threshold
otsu_thresh = get_range(otsu_thresh)
triangle_thresh = get_range(triangle_thresh)
manual_thresh = get_range(manual_thresh)
print(f"Otsu's Threshold: {otsu_thresh} nTriangle Threshold: {triangle_thresh} nManual Threshold: {manual_thresh}")
この結果:
Otsu's Threshold: (70.35, 139.65)
Triangle Threshold: (17.419999999999998, 34.58)
Manual Threshold: (105.18999999999998, 208.81)
これらはかなり違います! 前に見た値から、ここでは Triangle メソッドが最もうまく機能すると予想できます。 手動のしきい値は、ピクセル値の中央値を取得するだけであり、この画像の広い範囲にさらに乗算される高い基本しきい値を持つことになるため、あまり情報がありません。 Otsu の方法は、このことによる影響は少ないですが、それでも問題があります。
実行すると Canny()
これらのしきい値範囲でのメソッド:
edge_otsu = cv2.Canny(img_blur, *otsu_thresh)
edge_triangle = cv2.Canny(img_blur, *triangle_thresh)
edge_manual = cv2.Canny(img_blur, *manual_thresh)
fig, ax = plt.subplots(1, 3, figsize=(18, 6), dpi=150)
ax[0].imshow(edge_otsu, cmap='gray')
ax[1].imshow(edge_triangle, cmap='gray')
ax[2].imshow(edge_manual, cmap='gray')
注: この関数は複数の引数を想定しており、しきい値は XNUMX つのタプルです。 私たちはできる 構造を破壊する 接頭辞を付けてタプルを複数の引数に *
. これはリストとセットでも機能し、複数の引数をプログラムで取得した後に提供する優れた方法です。
この結果:
ここでは Triangle メソッドがうまく機能しました。 これは、他のケースでもうまく機能することを保証するものではありません。
cv2.Canny() を使用したビデオのリアルタイム エッジ検出
最後に、キャニーエッジ検出をリアルタイムでビデオに適用しましょう! を使用して、処理中のビデオを (各フレームごとに) 表示します。 cv2.imshow()
表示したいフレームを含むウィンドウを表示します。 ただし、後で調べて共有できるように、ビデオを MP4 ファイルに保存することもできます。
OpenCV を使用してビデオをロードするには、 VideoCapture()
方法。 私たちが通過する場合 0
– 現在のウェブカメラから記録されるため、ウェブカメラでもコードを実行できます! ファイル名を渡すと、ファイルがロードされます。
def edge_detection_video(filename):
cap = cv2.VideoCapture(filename)
fourcc = cv2.VideoWriter_fourcc(*'MP4V')
out = cv2.VideoWriter('output.mp4', fourcc, 30.0, (int(cap.get(3)), int(cap.get(4))), isColor=False)
while cap.isOpened():
(ret, frame) = cap.read()
if ret == True:
frame = cv2.GaussianBlur(frame, (3, 3), 0)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
edge = cv2.Canny(frame, 50, 100)
out.write(edge)
cv2.imshow('Edge detection', edge)
else:
break
if cv2.waitKey(10) & 0xFF == ord('q'):
break
cap.release()
out.release()
cv2.destroyAllWindows()
edge_detection_video('secret_video.mp4')
VideoWriter
出力ファイル名、FourCC (ビデオのエンコードに使用されるコーデックを示す XNUMX つのコーデック コード)、フレームレート、およびタプルとしての解像度など、いくつかのパラメーターを受け入れます。 ビデオを推測したりサイズを変更したりしないように、元のビデオの幅と高さを使用しました。 VideoCapture
幅、高さ、フレームの総数など、ビデオ自体に関するデータを含むインスタンス。
キャプチャが開かれている間、次のフレームを読み取ろうとします cap.read()
、結果コードと次のフレームを返します。 結果コードは True
or False
、次のフレームの存在またはその欠如を示します。 フレームがある場合にのみ、それをさらに処理しようとします。それ以外の場合は、ループを中断します。 有効なフレームごとに、ガウスぼかしを実行し、グレースケールに変換して実行します cv2.Canny()
その上に、 VideoWriter
を使用して表示します。 cv2.imshow()
ライブビュー用。
最後に、両方ともディスク上のファイルを操作しているため、キャプチャとビデオ ライターを解放し、既存のウィンドウをすべて破棄します。
メソッドを実行すると、 secret_video.mp4
入力 – ウィンドウがポップアップ表示され、完了すると、作業ディレクトリにファイルが作成されます。
まとめ
このガイドでは、キャニー エッジ検出のしくみとその構成要素 (ガウス スムージング、ソーベル フィルターと画像勾配、非最大抑制とヒステリシスしきい値) について説明しました。 最後に、キャニー エッジ検出のための自動しきい値範囲検索の方法を調べました。 cv2.Canny()
、およびビデオでこの手法を採用し、リアルタイムのエッジ検出を提供し、結果をビデオファイルに保存しました。
さらに先へ - コンピュータ ビジョンのための実用的な深層学習
あなたの好奇心旺盛な性質は、さらに先へ進みたいと思わせますか? 私たちは私たちをチェックアウトすることをお勧めします コース: 「Pythonによるコンピュータビジョンのための実践的な深層学習」.
別のコンピューター ビジョン コースですか?
MNIST 数字や MNIST ファッションの分類は行いません。 彼らはずっと前に彼らの役割を果たしました。 高度なブラックボックス アーキテクチャにパフォーマンスの負担を負わせる前に、あまりにも多くの学習リソースが基本的なデータセットと基本的なアーキテクチャに焦点を合わせています。
私たちが注目したいのは 謎解き, 実用性, 理解する, 直感 & 実際のプロジェクト. 学びたい の あなたは違いを生むことができますか? 私たちの脳が画像を処理する方法から、乳がんの研究レベルのディープ ラーニング分類器の作成、「幻覚」を起こすディープ ラーニング ネットワークまで、実際の作業を通じて原理と理論を教え、コンピュータビジョンを解決するためにディープラーニングを適用する専門家になるためのノウハウとツール。
内部には何がありますか?
- ビジョンの第一原理と、コンピューターに「見る」ことを教える方法
- コンピューター ビジョンのさまざまなタスクとアプリケーション
- あなたの仕事を楽にする貿易ツール
- コンピュータ ビジョンのためのデータセットの検索、作成、および利用
- 畳み込みニューラル ネットワークの理論と応用
- データセット内のドメイン シフト、共起、およびその他のバイアスの処理
- 転移学習と、他人のトレーニング時間と計算リソースを自分の利益のために利用する
- 最先端の乳がん分類器の構築とトレーニング
- 主流のアイデアに健全な懐疑心を適用し、広く採用されている手法の意味を理解する方法
- t-SNE と PCA を使用した ConvNet の「概念空間」の可視化
- 企業がコンピューター ビジョン技術を使用してより良い結果を達成する方法のケース スタディ
- 適切なモデル評価、潜在空間の可視化、モデルの注意の特定
- ドメイン調査の実施、独自のデータセットの処理、モデル テストの確立
- 最先端のアーキテクチャ、アイデアの進展、それらの独自性と実装方法
- KerasCV – 最先端のパイプラインとモデルを作成するための WIP ライブラリ
- 論文を解析して読み、自分で実装する方法
- 用途に応じた機種選定
- エンドツーエンドの機械学習パイプラインの作成
- Faster R-CNN、RetinaNet、SSD、YOLO を使用したオブジェクト検出のランドスケープと直感
- インスタンスとセマンティック セグメンテーション
- YOLOv5によるリアルタイム物体認識
- YOLOv5 オブジェクト検出器のトレーニング
- KerasNLP (業界で強力な WIP ライブラリ) を使用したトランスフォーマーの操作
- トランスフォーマーを ConvNets と統合して画像のキャプションを生成する
- DeepDream
- コンピューター ビジョンのためのディープ ラーニング モデルの最適化