最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C/C++的OpenCV 進(jìn)行圖像梯度提取的幾種實(shí)現(xiàn)

 更新時(shí)間:2025年05月31日 10:07:43   作者:whoarethenext  
本文主要介紹了C/C++的OpenCV 進(jìn)行圖像梯度提取的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

圖像梯度表示圖像中像素強(qiáng)度的變化率和方向。它是圖像分析中的一個(gè)基本概念,廣泛應(yīng)用于邊緣檢測(cè)、特征提取和物體識(shí)別等任務(wù)。OpenCV 提供了多種計(jì)算圖像梯度的函數(shù)。本文將介紹幾種常用的梯度算子及其在 C++/OpenCV 中的實(shí)現(xiàn)。

預(yù)備知識(shí)

在開(kāi)始之前,請(qǐng)確保您已經(jīng)安裝了 OpenCV,并且您的 C++ 開(kāi)發(fā)環(huán)境已經(jīng)配置好可以鏈接 OpenCV 庫(kù)。

通常,我們需要包含以下頭文件:

#include <opencv2/opencv.hpp> // 包含所有核心和contrib模塊
#include <iostream>

為方便起見(jiàn),我們也會(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 << "無(wú)法加載圖像: " << "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);

    // 接下來(lái)我們將對(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 以避免信息丟失。然后通過(guò) 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 方向的梯度組合起來(lái)得到總的梯度幅值。一個(gè)常見(jiàn)的方法是使用 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 類(lèi)型,然后使用 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 類(lèi)似,但它沒(méi)有 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 類(lèi)似:

    // ... 接上文 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ù)。它可以用來(lái)檢測(cè)邊緣,對(duì)噪聲比較敏感。Laplacian 算子通常通過(guò)檢測(cè)圖像中的零交叉點(diǎn)來(lái)定位邊緣。

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 位無(wú)符號(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 << "無(wú)法加載圖像: " << 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ì)算過(guò)程中的信息丟失,后續(xù)再通過(guò) 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/C++判斷素?cái)?shù)的三種方法

    C/C++判斷素?cái)?shù)的三種方法

    這篇文章主要給大家介紹了C/C++判斷素?cái)?shù)的三種方法,常規(guī)的函數(shù)判斷法,埃氏篩法和歐拉篩法這三種方法,并通過(guò)代碼示例講解的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下
    2023-12-12
  • C語(yǔ)言中的柔性數(shù)組你了解嗎

    C語(yǔ)言中的柔性數(shù)組你了解嗎

    這篇文章主要為大家詳細(xì)介紹了C99中的新語(yǔ)法——柔性數(shù)組的使用以及優(yōu)缺點(diǎn),文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的可以了解一下
    2023-04-04
  • C++實(shí)現(xiàn)坦克大戰(zhàn)小游戲EGE圖形界面

    C++實(shí)現(xiàn)坦克大戰(zhàn)小游戲EGE圖形界面

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)坦克大戰(zhàn)小游戲EGE圖形界面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • C++設(shè)計(jì)模式中的橋模式你了解嗎

    C++設(shè)計(jì)模式中的橋模式你了解嗎

    這篇文章主要為大家詳細(xì)介紹了C++設(shè)計(jì)模式中的橋模式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • C 語(yǔ)言中實(shí)現(xiàn)環(huán)形緩沖區(qū)

    C 語(yǔ)言中實(shí)現(xiàn)環(huán)形緩沖區(qū)

    本文主要是介紹 C語(yǔ)言實(shí)現(xiàn)環(huán)形緩沖區(qū),并附有詳細(xì)實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,希望能幫助有需要的小伙伴
    2016-07-07
  • C++中鏈表操作實(shí)例分析

    C++中鏈表操作實(shí)例分析

    本文給大家分享了個(gè)人對(duì)于C++中鏈表操作的理解,并對(duì)具體實(shí)例進(jìn)行了分析,是篇非常不錯(cuò)的學(xué)習(xí)鏈表操作的文章,希望大家能夠喜歡
    2017-07-07
  • c++作用域運(yùn)算符用法(全局變量和局部變量)

    c++作用域運(yùn)算符用法(全局變量和局部變量)

    這篇文章主要介紹了c++作用域運(yùn)算符用法,需要的朋友可以參考下
    2014-04-04
  • 深入探討:linux中遍歷文件夾下的所有文件

    深入探討:linux中遍歷文件夾下的所有文件

    本篇文章是對(duì)linux中遍歷文件夾下的所有文件進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C++基于遞歸和非遞歸算法求二叉樹(shù)鏡像的方法

    C++基于遞歸和非遞歸算法求二叉樹(shù)鏡像的方法

    這篇文章主要介紹了C++基于遞歸和非遞歸算法求二叉樹(shù)鏡像的方法,針對(duì)二叉樹(shù)遍歷結(jié)合實(shí)例形式分析了遞歸與非遞歸算法的實(shí)現(xiàn)與使用技巧,需要的朋友可以參考下
    2017-05-05
  • C/C++程序編譯流程詳解

    C/C++程序編譯流程詳解

    C/C++程序編譯過(guò)程包括下面4個(gè)階段:1.預(yù)處理,2.編譯,3.匯編,4.鏈接。下面我們就來(lái)詳細(xì)分析下這幾個(gè)階段。
    2016-04-04

最新評(píng)論

霞浦县| 忻城县| 东宁县| 额尔古纳市| 德钦县| 连江县| 林周县| 江安县| 泾源县| 武强县| 商洛市| 兴城市| 桐城市| 简阳市| 阳新县| 府谷县| 滕州市| 孟连| 宜城市| 措美县| 九寨沟县| 墨江| 宿松县| 泽州县| 崇信县| 钟山县| 贡山| 麦盖提县| 体育| 平乐县| 长兴县| 龙游县| 沁源县| 新邵县| 宝鸡市| 安乡县| 邢台县| 沅江市| 藁城市| 临猗县| 大丰市|