cv::Canny の使い方【OpenCV/C++】〜エッジ検出の基本と閾値の決め方〜
cv::Canny はグレースケール画像からエッジを抽出する関数です。ヒステリシス閾値を用いた Canny エッジ検出アルゴリズムを1行で呼び出せます。
cv::Canny(src, edges, threshold1, threshold2);
動作環境
- OpenCV 4.x
- コンパイル例:
g++ -std=c++17 main.cpp $(pkg-config --cflags --libs opencv4)
環境構築がまだの方は環境構築ガイドを先にどうぞ。
基本の使い方
#include <opencv2/opencv.hpp>
#include <iostream>
int main()
{
// グレースケールで読み込む
cv::Mat src = cv::imread("input.jpg", cv::IMREAD_GRAYSCALE);
if (src.empty()) {
std::cerr << "画像の読み込みに失敗しました" << std::endl;
return -1;
}
// ノイズ除去のためにガウシアンブラーをかける
cv::Mat blurred;
cv::GaussianBlur(src, blurred, cv::Size(5, 5), 1.4);
// Canny エッジ検出
cv::Mat edges;
cv::Canny(blurred, edges, 50.0, 150.0);
// 結果を保存
cv::imwrite("edges.jpg", edges);
std::cout << "エッジ画像を edges.jpg に保存しました" << std::endl;
return 0;
}
実行結果
エッジ画像を edges.jpg に保存しました
edges.jpg を確認すると、背景が黒・エッジ部分が白の2値画像が生成されています。
行ごとの解説
| 箇所 | 内容 |
|---|---|
cv::IMREAD_GRAYSCALE |
Canny はグレースケール入力が必須。カラー画像はここで変換する |
cv::GaussianBlur |
前処理でノイズを除去し、誤検出を減らす(後述) |
threshold1 = 50.0 |
弱エッジの下限閾値。これ未満のエッジ候補は除外される |
threshold2 = 150.0 |
強エッジの上限閾値。これ以上は確実にエッジと判定される |
出力 edges |
CV_8UC1・255=エッジ、0=非エッジ の2値画像 |
引数と戻り値
void cv::Canny(
InputArray image, // 入力: グレースケール画像(CV_8UC1)
OutputArray edges, // 出力: エッジ画像(CV_8UC1)
double threshold1, // ヒステリシス閾値(低い方)
double threshold2, // ヒステリシス閾値(高い方)
int apertureSize = 3, // Sobel フィルタのカーネルサイズ(3/5/7)
bool L2gradient = false // 勾配強度の計算式(false: L1, true: L2)
);
| 引数 | 型 | 説明 |
|---|---|---|
image |
InputArray |
グレースケール入力画像(必須) |
edges |
OutputArray |
エッジ出力画像。関数内で自動生成される |
threshold1 |
double |
低閾値。弱エッジの取捨選択に使用 |
threshold2 |
double |
高閾値。強エッジの確定に使用 |
apertureSize |
int |
Sobel カーネルサイズ。デフォルト 3 で大抵OK |
L2gradient |
bool |
true にすると精度が上がるが計算コスト増 |
閾値の比率の目安: threshold2 : threshold1 = 3 : 1 または 2 : 1 が一般的な出発点です。値を下げると細かいエッジも拾い、上げるとノイズが減ります。
実践例
例1: 大津の方法で閾値を自動決定する
閾値の手動調整が難しいケースでは、cv::threshold の大津法で推定した値を流用するテクニックが使えます。
#include <opencv2/opencv.hpp>
#include <iostream>
int main()
{
cv::Mat src = cv::imread("input.jpg", cv::IMREAD_GRAYSCALE);
if (src.empty()) {
std::cerr << "画像の読み込みに失敗しました" << std::endl;
return -1;
}
// 大津法で最適な閾値を取得(二値化画像は使わない)
cv::Mat dummy;
double otsu = cv::threshold(src, dummy, 0, 255,
cv::THRESH_BINARY | cv::THRESH_OTSU);
// 大津閾値を基準に Canny の閾値を決定
double low = otsu * 0.5;
double high = otsu;
std::cout << "Otsu: " << otsu << " low: " << low << " high: " << high << std::endl;
cv::Mat blurred, edges;
cv::GaussianBlur(src, blurred, cv::Size(5, 5), 1.4);
cv::Canny(blurred, edges, low, high);
cv::imwrite("edges_otsu.jpg", edges);
std::cout << "保存完了: edges_otsu.jpg" << std::endl;
return 0;
}
Otsu: 112 low: 56 high: 112
保存完了: edges_otsu.jpg
画像ごとに閾値を手動調整する手間を省けます。大津法の詳細は cv::threshold の使い方【OpenCV/C++】〜二値化と大津の方法〜 を参照してください。
例2: カラー画像のエッジを可視化する
エッジ検出後、元のカラー画像に重ねて表示したい場合の実装例です。
#include <opencv2/opencv.hpp>
#include <iostream>
int main()
{
cv::Mat src = cv::imread("input.jpg");
if (src.empty()) {
std::cerr << "画像の読み込みに失敗しました" << std::endl;
return -1;
}
// グレースケールに変換してから Canny を適用
cv::Mat gray, blurred, edges;
cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY);
cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 1.4);
cv::Canny(blurred, edges, 50.0, 150.0);
// エッジを緑色でカラー画像に重ねる
cv::Mat overlay = src.clone();
overlay.setTo(cv::Scalar(0, 255, 0), edges); // edges が mask 代わり
cv::imwrite("overlay.jpg", overlay);
std::cout << "保存完了: overlay.jpg" << std::endl;
return 0;
}
保存完了: overlay.jpg
overlay.jpg では、元画像の上にエッジ部分だけが緑色で描画されます。
つまずきポイント
⚠️ カラー画像をそのまま渡すと assertion エラーになる
cv::Canny の入力は CV_8UC1(グレースケール)限定です。CV_8UC3 のカラー画像を渡すと実行時に以下のエラーが出ます。
OpenCV Error: Assertion failed (src.type() == CV_8UC1) in Canny
cv::imread でカラー読み込みした場合は cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY) で変換してから渡してください。
⚠️ 前処理なしで Canny をかけると誤検出が多発する
カメラ画像や JPEG 圧縮を経た画像にはノイズが多く含まれます。前処理なしで Canny を適用するとノイズ部分もエッジとして検出され、結果がざらついた印象になります。
- 高周波ノイズ(カメラ等): cv::GaussianBlur の使い方【OpenCV/C++】 でぼかす
- 塩胡椒ノイズ(スキャン画像等): cv::medianBlur の使い方【OpenCV/C++】 でぼかす
ブラーのカーネルサイズを大きくするほどノイズは減りますが、細いエッジも潰れるのでトレードオフに注意してください。
⚠️ apertureSize を変えると閾値の効き方が変わる
apertureSize を 3→5→7 と大きくすると Sobel フィルタのスケールが変わり、同じ閾値でも検出されるエッジ数が変化します。apertureSize を変えたときは閾値の再調整が必要です。
関連する関数
- cv::GaussianBlur の使い方【OpenCV/C++】〜ガウシアンぼかしで画像を平滑化する〜
- cv::medianBlur の使い方【OpenCV/C++】〜メディアンフィルタで塩胡椒ノイズを除去する〜
- cv::threshold の使い方【OpenCV/C++】〜二値化と大津の方法〜
cv::Sobel/cv::Laplacian― エッジ強度を数値で取得したい場合に使う低レベル関数
まとめ
cv::Canny は前処理(GaussianBlur/medianBlur)と組み合わせることで安定したエッジ検出が可能です。閾値の出発点は threshold2 : threshold1 = 3 : 1 か大津法の流用が実務的です。入力は必ずグレースケール(CV_8UC1)で渡してください。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。

