cv::Sobel の使い方【OpenCV/C++】〜Sobelフィルタで勾配を求める〜
cv::Sobel は、画像の輝度勾配(エッジ強度・方向)を X 方向・Y 方向に分けて計算する関数です。エッジ検出の前処理や、方向依存の特徴抽出に広く使われます。最小限の呼び出しは次のとおりです。
cv::Sobel(src, dst, CV_16S, 1, 0, 3); // X方向の1次微分(カーネルサイズ3)
動作環境
- OpenCV 4.x
- コンパイル例:
g++ -std=c++17 main.cpp $(pkg-config --cflags --libs opencv4)
環境構築がまだの方は環境構築ガイドを先にどうぞ。
基本の使い方
X 方向・Y 方向それぞれの勾配を求め、合成して出力する基本パターンです。
#include <iostream>
#include <opencv2/opencv.hpp>
int main() {
cv::Mat src = cv::imread("input.png", cv::IMREAD_GRAYSCALE);
if (src.empty()) {
std::cerr << "画像の読み込みに失敗しました" << std::endl;
return -1;
}
// ノイズ低減のため事前にガウシアンブラーを適用
cv::Mat blurred;
cv::GaussianBlur(src, blurred, cv::Size(3, 3), 0);
// X・Y 方向の勾配を CV_16S で計算(符号付き16bit整数)
cv::Mat grad_x, grad_y;
cv::Sobel(blurred, grad_x, CV_16S, 1, 0, 3); // dx=1, dy=0
cv::Sobel(blurred, grad_y, CV_16S, 0, 1, 3); // dx=0, dy=1
// 絶対値を取って 8bit に変換
cv::Mat abs_x, abs_y;
cv::convertScaleAbs(grad_x, abs_x);
cv::convertScaleAbs(grad_y, abs_y);
// X・Y を合成してエッジ画像を生成
cv::Mat grad;
cv::addWeighted(abs_x, 0.5, abs_y, 0.5, 0, grad);
cv::imwrite("output_sobel.png", grad);
std::cout << "保存完了: output_sobel.png" << std::endl;
return 0;
}
実行結果
保存完了: output_sobel.png
output_sobel.png を確認すると、元画像のエッジ(輝度が急変する輪郭部分)が白く浮かび上がった画像が得られます。背景の平坦な領域は黒に近い値になります。
処理の流れ
- GaussianBlur でノイズを除去してから Sobel を適用するのが定石です。ノイズがあると微分演算が不安定になります(cv::GaussianBlur の解説記事)。
CV_16S(符号付き16bit整数) を出力型に指定します。8bit のまま受け取ると負の勾配値がクリップされて情報が失われます。cv::convertScaleAbsで絶対値を取りつつ 8bit にキャストします。cv::addWeightedで X・Y 成分を重み付き合成します。
引数と戻り値
| 引数 | 型 | 説明 |
|---|---|---|
src |
InputArray |
入力画像(8bit/16bit/32bit浮動小数点) |
dst |
OutputArray |
出力画像(ddepth で指定した型) |
ddepth |
int |
出力の深度(CV_16S / CV_32F / -1 で入力と同型) |
dx |
int |
X 方向の微分次数(0 or 1、dx+dy >= 1 が必須) |
dy |
int |
Y 方向の微分次数(0 or 1、dx+dy >= 1 が必須) |
ksize |
int |
Sobel カーネルサイズ(1 / 3 / 5 / 7、デフォルト 3) |
scale |
double |
微分値に掛けるスケール係数(デフォルト 1) |
delta |
double |
出力に加算するオフセット(デフォルト 0) |
borderType |
int |
境界処理方法(デフォルト cv::BORDER_DEFAULT) |
戻り値はありません(dst に結果が書き込まれます)。
ddepth の選び方
| 用途 | 推奨型 |
|---|---|
後続で convertScaleAbs → 可視化 |
CV_16S |
| 正確な勾配値を保持したい・合成演算を続ける | CV_32F |
cv::magnitude で勾配強度を求める |
CV_32F |
実践例
例1: 浮動小数点で勾配強度マップを生成する
#include <iostream>
#include <opencv2/opencv.hpp>
int main() {
cv::Mat src = cv::imread("input.png", cv::IMREAD_GRAYSCALE);
if (src.empty()) {
std::cerr << "画像の読み込みに失敗しました" << std::endl;
return -1;
}
cv::Mat blurred;
cv::GaussianBlur(src, blurred, cv::Size(3, 3), 0);
// CV_32F で勾配を計算
cv::Mat grad_x, grad_y;
cv::Sobel(blurred, grad_x, CV_32F, 1, 0, 3);
cv::Sobel(blurred, grad_y, CV_32F, 0, 1, 3);
// ユークリッド距離で勾配強度を計算
cv::Mat magnitude;
cv::magnitude(grad_x, grad_y, magnitude);
// 0〜255 に正規化して保存
cv::Mat magnitude_8u;
cv::normalize(magnitude, magnitude_8u, 0, 255, cv::NORM_MINMAX, CV_8U);
cv::imwrite("output_magnitude.png", magnitude_8u);
std::cout << "保存完了: output_magnitude.png" << std::endl;
return 0;
}
cv::magnitude を使うと √(Gx² + Gy²) を正確に計算できます。addWeighted による近似より精度が高く、特徴量計算に向いています。
例2: 水平エッジのみを強調する
#include <iostream>
#include <opencv2/opencv.hpp>
int main() {
cv::Mat src = cv::imread("input.png", cv::IMREAD_GRAYSCALE);
if (src.empty()) {
std::cerr << "画像の読み込みに失敗しました" << std::endl;
return -1;
}
cv::Mat blurred;
cv::GaussianBlur(src, blurred, cv::Size(3, 3), 0);
// Y方向の勾配のみ(水平方向のエッジが反応する)
cv::Mat grad_y;
cv::Sobel(blurred, grad_y, CV_16S, 0, 1, 3);
cv::Mat abs_y;
cv::convertScaleAbs(grad_y, abs_y);
cv::imwrite("output_horizontal_edge.png", abs_y);
std::cout << "保存完了: output_horizontal_edge.png" << std::endl;
return 0;
}
ドキュメント画像の罫線検出や、棚・段差の検出など、特定方向のエッジだけが欲しい場合は dx / dy を片方だけ 1 にします。
つまずきポイント
⚠️ ddepth を -1 や CV_8U にすると負の勾配が消える
cv::Sobel の結果は正負の値を持ちます。CV_8U(符号なし8bit)で受け取ると、負値が 0 にクリップされ、片方向のエッジが消えます。必ず CV_16S または CV_32F で受け取り、その後 cv::convertScaleAbs で絶対値変換してください。
// NG: 負の勾配が消える
cv::Sobel(src, dst, CV_8U, 1, 0, 3);
// OK
cv::Sobel(src, dst, CV_16S, 1, 0, 3);
cv::convertScaleAbs(dst, dst_abs);
⚠️ dx=0, dy=0 はアサーションエラーになる
dx と dy の合計が 1 以上でなければなりません。両方 0 を渡すと実行時アサーションが発生します。「X も Y も計算したい」場合は、2 回に分けて呼び出してください。
⚠️ ksize=1 は Sobel ではなく 3×1(または 1×3)の単純差分になる
カーネルサイズ 1 を指定すると、Sobel カーネルの平滑化成分がなくなり、単純な隣接差分フィルタとして動作します。ノイズに弱くなるため、通常は 3(デフォルト)以上を使います。ksize=3 で感度不足を感じる場合は 5 や 7 を試してください。
関連する関数
cv::GaussianBlur— Sobel の前処理として必須に近い平滑化フィルタ。詳しくはcv::GaussianBlur の使い方を参照。cv::medianBlur— 塩胡椒ノイズが多い場合はこちらが有効。cv::medianBlur の使い方も合わせてどうぞ。cv::Canny— Sobel を内部で使用する高精度エッジ検出器。閾値で二値化されたエッジが欲しい場合はこちらが便利。cv::magnitude— X・Y 勾配から勾配強度を正確に計算する関数。cv::convertScaleAbs— Sobel 出力を 8bit 可視化画像に変換する際に使用。cv::adaptiveThreshold— 勾配画像をさらに二値化する場合の選択肢。詳しくはcv::adaptiveThreshold の使い方を参照。
まとめ
cv::Sobelは X・Y 方向の輝度勾配を計算するフィルタで、エッジ検出の基本ブロックです。- 出力型は
CV_16SまたはCV_32Fを選び、cv::convertScaleAbsで可視化するのが定石です。 - 事前に
cv::GaussianBlurでノイズを除去することで、安定した結果が得られます。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。

