C/C++的OpenCV 進(jìn)行圖像梯度提取的幾種實(shí)現(xiàn)
圖像梯度表示圖像中像素強(qiáng)度的變化率和方向。它是圖像分析中的一個(gè)基本概念,廣泛應(yīng)用于邊緣檢測(cè)、特征提取和物體識(shí)別等任務(wù)。OpenCV 提供了多種計(jì)算圖像梯度的函數(shù)。本文將介紹幾種常用的梯度算子及其在 C++/OpenCV 中的實(shí)現(xiàn)。
預(yù)備知識(shí)
在開始之前,請(qǐng)確保您已經(jīng)安裝了 OpenCV,并且您的 C++ 開發(fā)環(huán)境已經(jīng)配置好可以鏈接 OpenCV 庫。
通常,我們需要包含以下頭文件:
#include <opencv2/opencv.hpp> // 包含所有核心和contrib模塊 #include <iostream>
為方便起見,我們也會(huì)使用 cv
命名空間:
using namespace cv; using namespace std;
1. 圖像加載與預(yù)處理
梯度計(jì)算通常在灰度圖像上進(jìn)行,因?yàn)轭伾畔?duì)于梯度方向的確定可能會(huì)引入不必要的復(fù)雜性。
int main(int argc, char** argv) { // 1. 加載圖像 // const char* filename = argc >= 2 ? argv[1] : "lena.jpg"; // 從命令行參數(shù)或默認(rèn)讀取 Mat src = imread("your_image.jpg", IMREAD_COLOR); // 請(qǐng)?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); // 接下來我們將對(duì) blurred_gray 或 gray 進(jìn)行操作
2. Sobel 算子
Sobel 算子是一種離散的一階微分算子,用于計(jì)算圖像亮度函數(shù)梯度的近似值。它結(jié)合了高斯平滑和微分求導(dǎo)。Sobel 算子分別計(jì)算水平方向(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
: 輸出圖像的深度。由于梯度值可能為負(fù),通常使用CV_16S
或CV_32F
以避免信息丟失。然后通過cv::convertScaleAbs
轉(zhuǎn)換為CV_8U
進(jìn)行顯示。dx
: x 方向上的差分階數(shù) (0 或 1)。dy
: y 方向上的差分階數(shù) (0 或 1)。ksize
: Sobel 核的大小,必須是 1, 3, 5 或 7。scale
: 可選的計(jì)算出的導(dǎo)數(shù)值的縮放因子。delta
: 可選的增量,在將結(jié)果存儲(chǔ)到dst
之前添加到結(jié)果中。borderType
: 像素外插方法。
計(jì)算 X 和 Y 方向的梯度
// ... 接上文 blurred_gray Mat grad_x, grad_y; Mat abs_grad_x, abs_grad_y; // 計(jì)算 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 并取絕對(duì)值 // 計(jì)算 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 方向的梯度組合起來得到總的梯度幅值。一個(gè)常見的方法是使用 cv::addWeighted
:
G = α ⋅ ∣ G x ∣ + β ⋅ ∣ G y ∣ + γ G = \alpha \cdot |G_x| + \beta \cdot |G_y| + \gammaG=α⋅∣Gx?∣+β⋅∣Gy?∣+γ
或者直接計(jì)算幅值 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)整,這里簡(jiǎn)單相加) addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad_combined); imshow("Sobel Combined Gradient", grad_combined);
更精確的幅值計(jì)算通常需要 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 算子是對(duì) Sobel 算子在核大小為 3x3 時(shí)的一種優(yōu)化。它具有更好的旋轉(zhuǎn)對(duì)稱性,因此在某些情況下可以提供比 3x3 Sobel 算子更準(zhǔn)確的結(jié)果。
cv::Scharr
函數(shù)原型與 cv::Sobel
類似,但它沒有 ksize
參數(shù),因?yàn)?Scharr 算子總是使用固定的 3x3 核。當(dāng) dx=1, dy=0
或 dx=0, dy=1
時(shí)使用。
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; // 計(jì)算 X 方向 Scharr 梯度 Scharr(blurred_gray, scharr_grad_x, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT); convertScaleAbs(scharr_grad_x, abs_scharr_grad_x); // 計(jì)算 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 (拉普拉斯) 算子是一種二階微分算子,它計(jì)算圖像的二階導(dǎo)數(shù)。它可以用來檢測(cè)邊緣,對(duì)噪聲比較敏感。Laplacian 算子通常通過檢測(cè)圖像中的零交叉點(diǎn)來定位邊緣。
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é)果中正值和負(fù)值都有意義(表示亮度的快速變化)。convertScaleAbs
將這些值轉(zhuǎn)換為適合顯示的 8 位無符號(hào)整數(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"; // 請(qǐng)?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; }
編譯與運(yùn)行
假設(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
不起作用,請(qǐng)根據(jù)您的 OpenCV 版本和安裝方式調(diào)整鏈接器標(biāo)志,例如 opencv
或特定模塊如 opencv_core opencv_imgproc opencv_highgui opencv_imgcodecs
)
總結(jié)
圖像梯度是圖像處理中的重要工具。Sobel、Scharr 和 Laplacian 算子是 OpenCV 中用于計(jì)算梯度的常用方法。
- Sobel 是最常用的,提供 x 和 y 方向的梯度。
- Scharr 在 3x3 核上通常比 Sobel 更精確。
- Laplacian 是二階導(dǎo)數(shù),對(duì)噪聲敏感,但可以直接給出邊緣信息。
選擇哪種算子取決于具體的應(yīng)用需求和圖像特性。通常,在計(jì)算梯度之前進(jìn)行高斯模糊可以幫助減少噪聲對(duì)結(jié)果的影響。同時(shí),注意輸出圖像深度 (ddepth
) 的選擇,以避免梯度計(jì)算過程中的信息丟失,后續(xù)再通過 convertScaleAbs
轉(zhuǎn)換到適合顯示的 CV_8U
格式。
到此這篇關(guān)于C/C++的OpenCV 進(jìn)行圖像梯度提取的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)OpenCV 圖像梯度提取內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++單例模式為何要實(shí)例化一個(gè)對(duì)象不全部使用static
這篇文章主要介紹了C++單例模式為何要實(shí)例化一個(gè)對(duì)象不全部使用static,文基于C++圍繞主題展開詳細(xì)內(nèi)容,需要的小伙伴可以參考一下2022-05-05深入const int *p與int * const p的區(qū)別詳解(常量指針與指向常量的指針)
本篇文章是對(duì)const int *p與int * const p的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06C++實(shí)現(xiàn)順序排序算法簡(jiǎn)單示例代碼
這篇文章主要介紹了C++實(shí)現(xiàn)順序排序算法簡(jiǎn)單示例代碼,對(duì)于學(xué)過C++的朋友一定不會(huì)陌生,現(xiàn)在重溫一下這個(gè)算法,需要的朋友可以參考下2014-08-08C語言中的運(yùn)算符優(yōu)先級(jí)和結(jié)合性一覽表
這篇文章主要介紹了C語言中的運(yùn)算符優(yōu)先級(jí)和結(jié)合性一覽表,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02C++實(shí)現(xiàn)LeetCode(85.最大矩形)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(85.最大矩形),本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07c# 實(shí)現(xiàn)獲取漢字十六進(jìn)制Unicode編碼字符串的實(shí)例
下面小編就為大家?guī)硪黄猚# 實(shí)現(xiàn)獲取漢字十六進(jìn)制Unicode編碼字符串的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-01-01