C/C++的OpenCV 進行圖像梯度提取的幾種實現(xiàn)
圖像梯度表示圖像中像素強度的變化率和方向。它是圖像分析中的一個基本概念,廣泛應(yīng)用于邊緣檢測、特征提取和物體識別等任務(wù)。OpenCV 提供了多種計算圖像梯度的函數(shù)。本文將介紹幾種常用的梯度算子及其在 C++/OpenCV 中的實現(xiàn)。
預(yù)備知識
在開始之前,請確保您已經(jīng)安裝了 OpenCV,并且您的 C++ 開發(fā)環(huán)境已經(jīng)配置好可以鏈接 OpenCV 庫。
通常,我們需要包含以下頭文件:
#include <opencv2/opencv.hpp> // 包含所有核心和contrib模塊 #include <iostream>
為方便起見,我們也會使用 cv 命名空間:
using namespace cv; using namespace std;
1. 圖像加載與預(yù)處理
梯度計算通常在灰度圖像上進行,因為顏色信息對于梯度方向的確定可能會引入不必要的復(fù)雜性。
int main(int argc, char** argv) {
// 1. 加載圖像
// const char* filename = argc >= 2 ? argv[1] : "lena.jpg"; // 從命令行參數(shù)或默認讀取
Mat src = imread("your_image.jpg", IMREAD_COLOR); // 請?zhí)鎿Q為您的圖片路徑
if (src.empty()) {
cout << "無法加載圖像: " << "your_image.jpg" << endl;
return -1;
}
// 2. 轉(zhuǎn)換為灰度圖
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
// 3. (可選)高斯模糊以減少噪聲,從而獲得更清晰的梯度
Mat blurred_gray;
GaussianBlur(gray, blurred_gray, Size(3, 3), 0, 0, BORDER_DEFAULT);
// 接下來我們將對 blurred_gray 或 gray 進行操作
2. Sobel 算子
Sobel 算子是一種離散的一階微分算子,用于計算圖像亮度函數(shù)梯度的近似值。它結(jié)合了高斯平滑和微分求導(dǎo)。Sobel 算子分別計算水平方向(Gx)和垂直方向(Gy)的梯度。
cv::Sobel 函數(shù)原型:
void Sobel( InputArray src, OutputArray dst, int ddepth,
int dx, int dy, int ksize = 3,
double scale = 1, double delta = 0,
int borderType = BORDER_DEFAULT );
src: 輸入圖像。dst: 輸出圖像,與輸入圖像大小和通道數(shù)相同。ddepth: 輸出圖像的深度。由于梯度值可能為負,通常使用CV_16S或CV_32F以避免信息丟失。然后通過cv::convertScaleAbs轉(zhuǎn)換為CV_8U進行顯示。dx: x 方向上的差分階數(shù) (0 或 1)。dy: y 方向上的差分階數(shù) (0 或 1)。ksize: Sobel 核的大小,必須是 1, 3, 5 或 7。scale: 可選的計算出的導(dǎo)數(shù)值的縮放因子。delta: 可選的增量,在將結(jié)果存儲到dst之前添加到結(jié)果中。borderType: 像素外插方法。
計算 X 和 Y 方向的梯度
// ... 接上文 blurred_gray
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y;
// 計算 X 方向梯度
// ddepth = CV_16S ???????????????????????? (overflow)
Sobel(blurred_gray, grad_x, CV_16S, 1, 0, 3, 1, 0, BORDER_DEFAULT);
convertScaleAbs(grad_x, abs_grad_x); // 轉(zhuǎn)換回 CV_8U 并取絕對值
// 計算 Y 方向梯度
Sobel(blurred_gray, grad_y, CV_16S, 0, 1, 3, 1, 0, BORDER_DEFAULT);
convertScaleAbs(grad_y, abs_grad_y);
// 顯示 X 和 Y 方向的梯度
imshow("Sobel X Gradient", abs_grad_x);
imshow("Sobel Y Gradient", abs_grad_y);
合并梯度
通常,我們將 X 和 Y 方向的梯度組合起來得到總的梯度幅值。一個常見的方法是使用 cv::addWeighted:
G = α ⋅ ∣ G x ∣ + β ⋅ ∣ G y ∣ + γ G = \alpha \cdot |G_x| + \beta \cdot |G_y| + \gammaG=α⋅∣Gx?∣+β⋅∣Gy?∣+γ
或者直接計算幅值 G = G x 2 + G y 2 G = \sqrt{G_x^2 + G_y^2}G=Gx2?+Gy2??。cv::addWeighted 提供了一種近似方法。
Mat grad_combined;
// 近似梯度幅值 (權(quán)重可以調(diào)整,這里簡單相加)
addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad_combined);
imshow("Sobel Combined Gradient", grad_combined);
更精確的幅值計算通常需要 grad_x 和 grad_y 為 CV_32F 類型,然后使用 cv::magnitude。
Mat grad_x_f, grad_y_f;
Sobel(blurred_gray, grad_x_f, CV_32F, 1, 0, 3);
Sobel(blurred_gray, grad_y_f, CV_32F, 0, 1, 3);
Mat magnitude, angle;
cartToPolar(grad_x_f, grad_y_f, magnitude, angle, true); // angle in degrees
Mat abs_magnitude;
convertScaleAbs(magnitude, abs_magnitude);
imshow("Sobel Magnitude Precise", abs_magnitude);
3. Scharr 算子
Scharr 算子是對 Sobel 算子在核大小為 3x3 時的一種優(yōu)化。它具有更好的旋轉(zhuǎn)對稱性,因此在某些情況下可以提供比 3x3 Sobel 算子更準確的結(jié)果。
cv::Scharr 函數(shù)原型與 cv::Sobel 類似,但它沒有 ksize 參數(shù),因為 Scharr 算子總是使用固定的 3x3 核。當 dx=1, dy=0 或 dx=0, dy=1 時使用。
void Scharr( InputArray src, OutputArray dst, int ddepth,
int dx, int dy, double scale = 1, double delta = 0,
int borderType = BORDER_DEFAULT );
使用方法與 Sobel 類似:
// ... 接上文 blurred_gray
Mat scharr_grad_x, scharr_grad_y;
Mat abs_scharr_grad_x, abs_scharr_grad_y;
// 計算 X 方向 Scharr 梯度
Scharr(blurred_gray, scharr_grad_x, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT);
convertScaleAbs(scharr_grad_x, abs_scharr_grad_x);
// 計算 Y 方向 Scharr 梯度
Scharr(blurred_gray, scharr_grad_y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT);
convertScaleAbs(scharr_grad_y, abs_scharr_grad_y);
// 顯示 Scharr 梯度
imshow("Scharr X Gradient", abs_scharr_grad_x);
imshow("Scharr Y Gradient", abs_scharr_grad_y);
Mat scharr_grad_combined;
addWeighted(abs_scharr_grad_x, 0.5, abs_scharr_grad_y, 0.5, 0, scharr_grad_combined);
imshow("Scharr Combined Gradient", scharr_grad_combined);
4. Laplacian 算子
Laplacian (拉普拉斯) 算子是一種二階微分算子,它計算圖像的二階導(dǎo)數(shù)。它可以用來檢測邊緣,對噪聲比較敏感。Laplacian 算子通常通過檢測圖像中的零交叉點來定位邊緣。
cv::Laplacian 函數(shù)原型:
void Laplacian( InputArray src, OutputArray dst, int ddepth,
int ksize = 1, double scale = 1, double delta = 0,
int borderType = BORDER_DEFAULT );
ksize: 拉普拉斯核的大小,必須是正奇數(shù)。通常是 1 或 3。
// ... 接上文 blurred_gray
Mat laplacian_dst;
Mat abs_laplacian_dst;
Laplacian(blurred_gray, laplacian_dst, CV_16S, 3, 1, 0, BORDER_DEFAULT); // ksize=3
convertScaleAbs(laplacian_dst, abs_laplacian_dst);
imshow("Laplacian Operator", abs_laplacian_dst);
由于拉普拉斯算子是二階導(dǎo)數(shù),它的結(jié)果中正值和負值都有意義(表示亮度的快速變化)。convertScaleAbs 將這些值轉(zhuǎn)換為適合顯示的 8 位無符號整數(shù)。
5. 顯示結(jié)果與程序結(jié)束
在 main 函數(shù)的末尾,添加等待按鍵和關(guān)閉窗口的調(diào)用:
// ... 所有 imshow 調(diào)用之后
cout << "按任意鍵退出..." << endl;
waitKey(0);
destroyAllWindows();
return 0;
}
完整示例代碼
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
// 1. 加載圖像
const char* filename = "your_image.jpg"; // 請?zhí)鎿Q為您的圖片路徑
Mat src = imread(filename, IMREAD_COLOR);
if (src.empty()) {
cout << "無法加載圖像: " << filename << endl;
return -1;
}
imshow("Original Image", src);
// 2. 轉(zhuǎn)換為灰度圖
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
imshow("Grayscale Image", gray);
// 3. (可選)高斯模糊以減少噪聲
Mat blurred_gray;
GaussianBlur(gray, blurred_gray, Size(3, 3), 0, 0, BORDER_DEFAULT);
imshow("Blurred Grayscale", blurred_gray);
// --- Sobel 梯度 ---
Mat grad_x_sobel, grad_y_sobel;
Mat abs_grad_x_sobel, abs_grad_y_sobel;
Mat grad_combined_sobel;
Sobel(blurred_gray, grad_x_sobel, CV_16S, 1, 0, 3, 1, 0, BORDER_DEFAULT);
convertScaleAbs(grad_x_sobel, abs_grad_x_sobel);
Sobel(blurred_gray, grad_y_sobel, CV_16S, 0, 1, 3, 1, 0, BORDER_DEFAULT);
convertScaleAbs(grad_y_sobel, abs_grad_y_sobel);
addWeighted(abs_grad_x_sobel, 0.5, abs_grad_y_sobel, 0.5, 0, grad_combined_sobel);
imshow("Sobel X Gradient", abs_grad_x_sobel);
imshow("Sobel Y Gradient", abs_grad_y_sobel);
imshow("Sobel Combined Gradient", grad_combined_sobel);
// --- Scharr 梯度 ---
Mat grad_x_scharr, grad_y_scharr;
Mat abs_grad_x_scharr, abs_grad_y_scharr;
Mat grad_combined_scharr;
Scharr(blurred_gray, grad_x_scharr, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT);
convertScaleAbs(grad_x_scharr, abs_grad_x_scharr);
Scharr(blurred_gray, grad_y_scharr, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT);
convertScaleAbs(grad_y_scharr, abs_grad_y_scharr);
addWeighted(abs_grad_x_scharr, 0.5, abs_grad_y_scharr, 0.5, 0, grad_combined_scharr);
imshow("Scharr X Gradient", abs_grad_x_scharr);
imshow("Scharr Y Gradient", abs_grad_y_scharr);
imshow("Scharr Combined Gradient", grad_combined_scharr);
// --- Laplacian 梯度 ---
Mat laplacian_dst;
Mat abs_laplacian_dst;
Laplacian(blurred_gray, laplacian_dst, CV_16S, 3, 1, 0, BORDER_DEFAULT);
convertScaleAbs(laplacian_dst, abs_laplacian_dst);
imshow("Laplacian Operator", abs_laplacian_dst);
cout << "按任意鍵退出..." << endl;
waitKey(0);
destroyAllWindows();
return 0;
}
編譯與運行
假設(shè)您已正確安裝 OpenCV,可以使用 g++ 編譯上述代碼:
g++ your_code_file.cpp -o gradient_extraction $(pkg-config --cflags --libs opencv4) ./gradient_extraction your_image.jpg
(如果 pkg-config --libs opencv4 不起作用,請根據(jù)您的 OpenCV 版本和安裝方式調(diào)整鏈接器標志,例如 opencv 或特定模塊如 opencv_core opencv_imgproc opencv_highgui opencv_imgcodecs)
總結(jié)
圖像梯度是圖像處理中的重要工具。Sobel、Scharr 和 Laplacian 算子是 OpenCV 中用于計算梯度的常用方法。
- Sobel 是最常用的,提供 x 和 y 方向的梯度。
- Scharr 在 3x3 核上通常比 Sobel 更精確。
- Laplacian 是二階導(dǎo)數(shù),對噪聲敏感,但可以直接給出邊緣信息。
選擇哪種算子取決于具體的應(yīng)用需求和圖像特性。通常,在計算梯度之前進行高斯模糊可以幫助減少噪聲對結(jié)果的影響。同時,注意輸出圖像深度 (ddepth) 的選擇,以避免梯度計算過程中的信息丟失,后續(xù)再通過 convertScaleAbs 轉(zhuǎn)換到適合顯示的 CV_8U 格式。
到此這篇關(guān)于C/C++的OpenCV 進行圖像梯度提取的實現(xiàn)的文章就介紹到這了,更多相關(guān)OpenCV 圖像梯度提取內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于matlab實現(xiàn)DCT數(shù)字水印嵌入與提取
數(shù)字水印技術(shù)是將一些標識信息直接嵌入數(shù)字載體當中,?或間接表示在信號載體中,?且不影響原載體的使用價值。本文主要為大家介紹了基于matlab如何實現(xiàn)數(shù)字水印的嵌入與提取,感興趣的可以學(xué)習(xí)一下2022-01-01
關(guān)于C++復(fù)制構(gòu)造函數(shù)的實現(xiàn)講解
今天小編就為大家分享一篇關(guān)于關(guān)于C++復(fù)制構(gòu)造函數(shù)的實現(xiàn)講解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12
C++ CryptoPP使用AES實現(xiàn)加解密詳解
Crypto++ (CryptoPP) 是一個用于密碼學(xué)和加密的 C++ 庫,提供了大量的密碼學(xué)算法和功能,這篇文章主要為大家介紹了C++ CryptoPP如何使用AES實現(xiàn)加解密,需要的可以參考下2023-11-11
Matlab制作視頻并轉(zhuǎn)換成gif動態(tài)圖的兩種方法
這篇文章主要介紹了Matlab制作視頻并轉(zhuǎn)換成gif動態(tài)圖的兩種方法,第一種方法使用movie(f)直接取生成AVI視頻文件,相對來說比較簡單,需要的朋友可以參考下2018-08-08
Visual Studio 2022配置fftw第三方庫的詳細過程
FFTW是一個可以進行可變長度一維或多維DFT的開源C程序庫,是目前最快的FFT算法實現(xiàn),本文簡述了在Windows平臺上,如何在C++中調(diào)用FFTW,所使用的IDE為Visual Studio 2022,感興趣的朋友一起看看吧2024-06-06

