cv::Laplacian の使い方【OpenCV/C++】〜ラプラシアンフィルタでエッジを検出する〜
cv::Laplacian は画像の二次微分を計算してエッジを検出する関数です。Sobel フィルタのような一次微分と異なり、輝度の変化率そのものではなく「変化の急峻さ」に反応するため、細い線や点状の特徴を拾いやすい特性があります。
最小限の呼び出しはこれだけです。
cv::Laplacian(src, dst, CV_16S, 3);
動作環境
- OpenCV 4.x
- ビルド・実行例(Linux):
g++ -std=c++17 main.cpp `pkg-config --cflags --libs opencv4` -o main && ./main
環境構築がまだの方は環境構築ガイドを先にどうぞ。
基本の使い方
#include <opencv2/opencv.hpp>
#include <iostream>
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_16S で符号付きに)
cv::Mat lap16s;
cv::Laplacian(blurred, lap16s, CV_16S, 3);
// 絶対値を取って CV_8U に変換
cv::Mat dst;
cv::convertScaleAbs(lap16s, dst);
cv::imwrite("output.png", dst);
std::cout << "保存しました: output.png" << std::endl;
return 0;
}
実行結果
保存しました: output.png
output.png を確認すると、元画像の輝度変化が急な箇所(輪郭・テクスチャの境界)が白く浮かび上がったエッジ画像になっています。背景のように輝度が均一な領域はほぼ黒になります。
コード解説
| 行 | 内容 |
|---|---|
cv::imread(..., IMREAD_GRAYSCALE) |
ラプラシアンはグレースケール画像に適用するのが基本 |
cv::GaussianBlur |
ノイズが残ったままだとノイズにも激しく反応するため、前処理は必須 |
cv::Laplacian(..., CV_16S, 3) |
出力を符号付き 16bit に。CV_8U で受けると負の値が欠落する |
cv::convertScaleAbs |
符号付き値の絶対値を取り、表示・保存に使える CV_8U へ変換 |
引数と戻り値
void cv::Laplacian(
InputArray src,
OutputArray dst,
int ddepth,
int ksize = 1,
double scale = 1,
double delta = 0,
int borderType = BORDER_DEFAULT
);
| 引数 | 型 | 説明 |
|---|---|---|
src |
InputArray |
入力画像(グレースケール推奨) |
dst |
OutputArray |
出力画像。サイズ・チャンネル数は src と同じ |
ddepth |
int |
出力のビット深度。CV_16S または CV_32F を推奨 |
ksize |
int |
カーネルサイズ(奇数)。1 の場合は 3×3 の単純なラプラシアンカーネル相当 |
scale |
double |
計算結果に乗じるスケール係数(デフォルト 1) |
delta |
double |
計算結果に加算するオフセット値(デフォルト 0) |
borderType |
int |
境界処理方法。通常はデフォルトのまま |
⚠️ ddepth に CV_8U を指定すると負の微分値がすべてクリップされ、エッジが半分しか検出されません。必ず CV_16S か CV_32F を使ってください。
実践例
実践例 1: エッジ強調画像の生成
エッジ画像を元画像に加算してシャープネスを強調するテクニックです。ラプラシアンの出力を元画像から引くことでシャープネス強調が得られます。
#include <opencv2/opencv.hpp>
#include <iostream>
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::Mat lap16s;
cv::Laplacian(blurred, lap16s, CV_16S, 3);
// CV_16S → CV_32F に変換して元画像と差し引き
cv::Mat lap32f, src32f, sharpened32f;
lap16s.convertTo(lap32f, CV_32F);
src.convertTo(src32f, CV_32F);
// Laplacian of Gaussian (LoG) シャープネス強調: 元画像 - ラプラシアン
sharpened32f = src32f - lap32f;
// 値域クリップして CV_8U に変換
cv::Mat sharpened;
sharpened32f.convertTo(sharpened, CV_8U, 1.0, 0.0);
cv::imwrite("sharpened.png", sharpened);
std::cout << "シャープ画像を保存しました: sharpened.png" << std::endl;
return 0;
}
実践例 2: カラー画像のエッジ検出
カラー画像に対してラプラシアンを適用する場合は、チャンネル別に処理するか、グレースケールに変換してから処理します。以下はグレースケール変換後に処理し、結果を擬似カラー表示する例です。
#include <opencv2/opencv.hpp>
#include <iostream>
int main()
{
cv::Mat srcColor = cv::imread("input.png", cv::IMREAD_COLOR);
if (srcColor.empty()) {
std::cerr << "画像を読み込めませんでした" << std::endl;
return 1;
}
// グレースケールに変換してからエッジ検出
cv::Mat gray;
cv::cvtColor(srcColor, gray, cv::COLOR_BGR2GRAY);
cv::Mat blurred;
cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 0);
cv::Mat lap16s;
cv::Laplacian(blurred, lap16s, CV_16S, 3);
cv::Mat lapAbs;
cv::convertScaleAbs(lap16s, lapAbs);
// 擬似カラー表示
cv::Mat colored;
cv::applyColorMap(lapAbs, colored, cv::COLORMAP_JET);
cv::imwrite("edge_colored.png", colored);
std::cout << "擬似カラーエッジ画像を保存しました: edge_colored.png" << std::endl;
return 0;
}
つまずきポイント
⚠️ ddepth を CV_8U にすると負のエッジが消える
ラプラシアンは二次微分なので、明るい領域から暗い領域への変化では負の値が出ます。ddepth = CV_8U のままだと負の値がゼロにクリップされ、エッジが片側しか検出されません。CV_16S か CV_32F で受け、その後 cv::convertScaleAbs で絶対値変換するのが定石です。
⚠️ ノイズに非常に敏感
ラプラシアンは二次微分であるため、一次微分の Sobel よりもノイズへの感度が高くなります。事前のガウシアンブラーを省くと、センサーノイズや圧縮アーティファクトにも激しく反応し、エッジとノイズの区別がつかなくなります。cv::GaussianBlur との組み合わせは必須と考えてください。
⚠️ ksize=1 と ksize=3 の違い
ksize=1 は次の 3×3 カーネルを使います。
0 1 0
1 -4 1
0 1 0
ksize=3 以上を指定すると内部で cv::Sobel を二回適用した近似カーネルに切り替わり、応答特性が変わります。用途に応じて比較検討してください。
関連する関数
- cv::GaussianBlur の使い方【OpenCV/C++】 — ラプラシアンの前処理として組み合わせることがほぼ必須
- cv::medianBlur の使い方【OpenCV/C++】 — 塩胡椒ノイズが多い場合の代替前処理
- cv::adaptiveThreshold の使い方【OpenCV/C++】 — エッジ検出結果をさらに二値化するときに活用
cv::Sobel— 一次微分によるエッジ検出。ラプラシアンと使い比べる対象cv::Canny— ヒステリシス処理を含む、より高精度なエッジ検出器
まとめ
cv::Laplacian は二次微分によるエッジ検出フィルタです。ddepth は必ず CV_16S か CV_32F を指定し、cv::convertScaleAbs で絶対値変換してから使います。ノイズへの感度が高いため、cv::GaussianBlur との前処理セットで使うことを強くおすすめします。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。

