圖像處理之邊緣檢測(C++)
文章目錄
- 圖像處理之邊緣檢測(C++)
- 前言
- 一、Roberts算子
- 1.原理
- 2.代碼實現
- 二、Sobel算子
- 1.原理
- 2.代碼實現
- 三、Prewitt算子
- 1.原理
- 2.代碼實現
- 四、Laplacian算子
- 1.原理
- 2.代碼實現
- 五、LOG算子
- 1.原理
- 2.代碼實現
- 六、DOG算子
- 1.原理
- 2.代碼實現
- 總結
前言
邊緣檢測是圖像處理的基礎,邊緣檢測主要是為了找到圖像中亮度變化劇烈的像素點構成的集合,即圖像的輪廓(邊緣)。
傳統邊緣檢測的主要有Roberts、Sobel、Prewitt、Laplacian、LOG、DOG等方法,本文介紹上述的邊緣檢測算子的實現。
原圖:
一、Roberts算子
1.原理
Roberts算子是一種斜向偏差分的梯度計算方法,采用對角方向相鄰的兩像素值之差,梯度的大小代表邊緣的強度,梯度的方向與邊緣的走向垂直。它是2X2算子模板。2個卷積核形成了Roberts算子。圖像中的每一個點都用這2個核做卷積。
卷積核形式如下:
計算公式如下:
備注:采用絕對值相加減少計算量。
2.代碼實現
#include <opencv.hpp>
#include <iostream>/*
* @param const cv::Mat& src 輸入圖像
* @param cv::Mat& dst 輸出圖像
*/
void roberts(const cv::Mat& src, cv::Mat& dst)
{// 創建卷積核cv::Mat kernel_x = (cv::Mat_<float>(2, 2) << 1, 0, 0, -1);cv::Mat kernel_y = (cv::Mat_<float>(2, 2) << 0, 1, -1, 0);// 卷積計算// 卷積核翻轉cv::flip(kernel_x, kernel_x, -1);cv::flip(kernel_y, kernel_y, -1);// 相關性計算cv::Mat dst_x, dst_y;cv::filter2D(src, dst_x, kernel_x.depth(), kernel_x);cv::filter2D(src, dst_y, kernel_y.depth(), kernel_y);// 絕對值轉換cv::convertScaleAbs(dst_x, dst_x);cv::convertScaleAbs(dst_y, dst_y);// 計算梯度dst = dst_x + dst_y;cv::normalize(dst, dst, 0, 255, cv::NORM_MINMAX,CV_8UC1);
}int main()
{// 讀取圖片std::string filepath = "F://work_study//algorithm_demo//regionGrow_test.jpg";cv::Mat src = cv::imread(filepath, cv::IMREAD_GRAYSCALE);if (src.empty()){return -1;}cv::Mat dst;roberts(src, dst);// 保存圖片cv::imwrite("dst.jpg", dst);system("pause");return 0;
}
二、Sobel算子
1.原理
Sobel算子根據像素點上下、左右鄰點灰度加權差,在邊緣處達到極值這一現象檢測邊緣。它結合高斯平滑和微分求導,用來計算圖像灰度函數的近似梯度(讀者可以嘗試分離卷積核,即可看出卷積核是由一個求解水平梯度或者垂直梯度的卷積核和非歸一化的高斯平滑相乘得到)。在圖像的任何一點使用此算子,將會產生對應的梯度矢量或是其法矢量。它對噪聲具有平滑作用,提供較為精確的邊緣方向信息,邊緣定位精度不夠高。
卷積核形式:
計算公式如下:
2.代碼實現
/*
* @param const cv::Mat& src 輸入圖像
* @param cv::Mat& dst 輸出圖像
* @brief sobel算子實現
*/
void sobel(const cv::Mat& src, cv::Mat& dst) {// 創建卷積核cv::Mat kernel_x = (cv::Mat_<float>(3,3) << -1, 0, 1, -2, 0, 2, -1, 0, 1);cv::Mat kernel_y = (cv::Mat_<float>(3, 3) << 1, 2, 1, 0, 0, 0, -1, -2, -1);// 卷積運算// 翻轉卷積核cv::flip(kernel_x, kernel_x, -1);cv::flip(kernel_y, kernel_y, -1);// 相關性計算cv::Mat dst_x, dst_y;cv::filter2D(src, dst_x, kernel_x.depth(), kernel_x);cv::filter2D(src, dst_y, kernel_y.depth(), kernel_y);cv::convertScaleAbs(dst_x, dst_x);cv::convertScaleAbs(dst_y, dst_y);cv::normalize(dst_x + dst_y, dst, 0, 255, cv::NORM_MINMAX, CV_8U);
}int main()
{// 讀取圖片std::string filepath = "F://work_study//algorithm_demo//regionGrow_test.jpg";cv::Mat src = cv::imread(filepath, cv::IMREAD_GRAYSCALE);if (src.empty()){return -1;}cv::Mat dst;sobel(src, dst);// 保存圖片cv::imwrite("dst.jpg", dst);system("pause");return 0;
}
三、Prewitt算子
1.原理
Prewitt算子的計算步驟如sobel算子,將方向的差分運算和局部平均相結合的方法(通過分離卷積核即可得出結論,不多贅述),也是取水平和垂直兩個卷積核來分別對圖像中各個像素點做卷積運算,所不同的是,Sobel 算子是先做加權平均然后再微分,Prewitt 算子是先平均后求微分。由于采用了局部灰度平均,容易檢測出偽邊緣,且邊緣定位精度較低。
卷積核形式:
計算公式:
2.代碼實現
/*
* @param const cv::Mat& src 輸入圖像
* @param cv::Mat& dst 輸出圖像
* @brief prewitt算子實現
*/
void prewitt(const cv::Mat& src, cv::Mat& dst) {// 創建卷積核cv::Mat kernel_x = (cv::Mat_<float>(3, 3) << -1, 0, 1, -1, 0, 1, -1, 0, 1);cv::Mat kernel_y = (cv::Mat_<float>(3, 3) << 1, 1, 1, 0, 0, 0, -1, -1, -1);// 卷積運算// 翻轉卷積核cv::flip(kernel_x, kernel_x, -1);cv::flip(kernel_y, kernel_y, -1);// 相關性計算cv::Mat dst_x, dst_y;cv::filter2D(src, dst_x, kernel_x.depth(), kernel_x);cv::filter2D(src, dst_y, kernel_y.depth(), kernel_y);cv::convertScaleAbs(dst_x, dst_x);cv::convertScaleAbs(dst_y, dst_y);cv::normalize(dst_x + dst_y, dst, 0, 255, cv::NORM_MINMAX, CV_8U);
}int main()
{// 讀取圖片std::string filepath = "F://work_study//algorithm_demo//regionGrow_test.jpg";cv::Mat src = cv::imread(filepath, cv::IMREAD_GRAYSCALE);if (src.empty()){return -1;}cv::Mat dst;prewitt(src, dst);// 保存圖片cv::imwrite("prewitt_dst.jpg", dst);system("pause");return 0;
}
四、Laplacian算子
1.原理
它不依賴于邊緣方向的二階微分算子,對圖像中的階躍型邊緣點定位準確,該算子對噪聲非常敏感,它使噪聲成分得到加強,這兩個特性使得該算子容易丟失一部分邊緣的方向信息,造成一些不連續的檢測邊緣,同時抗噪聲能力比較差,由于其算法可能會出現雙像素邊界,常用來判斷邊緣像素位于圖像的明區或暗區。
2.代碼實現
/*
* @param const cv::Mat& src 輸入圖像
* @param cv::Mat& dst 輸出圖像
* @brief prewitt算子實現
*/
void laplacian(const cv::Mat& src, cv::Mat& dst) {// 創建卷積核//cv::Mat kernel = (cv::Mat_<float>(3, 3) << 0, 1, 0, 1, -4, 1, 0, 1, 0);cv::Mat kernel = (cv::Mat_<float>(3, 3) << 1, 1, 1, 1, -8, 1, 1, 1, 1);// 卷積運算// 翻轉卷積核cv::flip(kernel, kernel, -1);// 相關性計算cv::filter2D(src, dst, kernel.depth(), kernel);cv::convertScaleAbs(dst, dst);cv::normalize(dst, dst, 0, 255, cv::NORM_MINMAX, CV_8U);
}int main()
{// 讀取圖片std::string filepath = "F://work_study//algorithm_demo//regionGrow_test.jpg";cv::Mat src = cv::imread(filepath, cv::IMREAD_GRAYSCALE);if (src.empty()){return -1;}cv::Mat dst;laplacian(src, dst);// 保存圖片cv::imwrite("laplacian.jpg", dst);system("pause");return 0;
}
五、LOG算子
1.原理
高斯拉普拉斯算子Lapacian of gaussian先對圖像進行高斯平滑濾波處理,然后再與Laplacian算子進行卷積。其處理過程:灰度-高斯-拉普拉斯-負值為0。
2.代碼實現
/*
* @param const cv::Mat& src 輸入圖像
* @param cv::Mat& dst 輸出圖像
* @brief LOG算子實現
*/
void LOG(const cv::Mat& src, cv::Mat& dst) {// 創建卷積核cv::Mat kernel = (cv::Mat_<float>(5, 5) << 0, 0, -1, 0, 0,0, -1, -2, -1, 0,-1, -2, 16, -2, -1,0, -1, -2, -1, 0,0, 0, -1, 0, 0);// 卷積運算(卷積核對稱無需翻轉)cv::filter2D(src, dst, kernel.depth(), kernel);cv::threshold(dst, dst, 0, 255, cv::THRESH_BINARY);dst.convertTo(dst, CV_8U);
}/*
* @param const cv::Mat& src 輸入圖像
* @param cv::Mat& dst 輸出圖像
* @brief LOG算子實現
*/
void LOG1(const cv::Mat& src, cv::Mat& dst) {// 高斯濾波cv::GaussianBlur(src, dst, cv::Size(5, 5),0.5);// laplacianlaplacian(dst, dst);cv::threshold(dst, dst, 0, 255, cv::THRESH_BINARY);dst.convertTo(dst, CV_8U);
}int main()
{// 讀取圖片std::string filepath = "F://work_study//algorithm_demo//regionGrow_test.jpg";cv::Mat src = cv::imread(filepath, cv::IMREAD_GRAYSCALE);if (src.empty()){return -1;}cv::Mat dst;LOG(src, dst);// 保存圖片cv::imwrite("LOG.jpg", dst);system("pause");return 0;
}
六、DOG算子
1.原理
Difference of Gaussian(DOG)是高斯函數的差分。它是可以通過將圖像與高斯函數進行卷積得到一幅圖像的低通濾波結果,它對高斯拉普拉斯LoG的近似,在某一尺度上的特征檢測可以通過對兩個相鄰高斯尺度空間的圖像相減,得到DoG的響應值圖像。
公式證明:
計算步驟:
使用兩個不同標準差的高斯核平滑圖像,然后結果相減,負值為0,獲得邊緣檢測結果。
2.代碼實現
*
* @param const cv::Mat& src 輸入圖像
* @param cv::Mat& dst 輸出圖像
* @brief DOG算子實現
*/
void DOG(const cv::Mat& src, cv::Mat& dst,double sigma,int k) {cv::Mat dst_x, dst_y;cv::GaussianBlur(src, dst_x, cv::Size(3, 3), sigma);cv::GaussianBlur(src, dst_y, cv::Size(3, 3), k*sigma);dst = dst_x - dst_y;cv::threshold(dst, dst, 0, 255, cv::THRESH_BINARY);dst.convertTo(dst, CV_8U);
}int main()
{// 讀取圖片std::string filepath = "F://work_study//algorithm_demo//regionGrow_test.jpg";cv::Mat src = cv::imread(filepath, cv::IMREAD_GRAYSCALE);if (src.empty()){return -1;}cv::Mat dst;DOG(src, dst,1,2);// 保存圖片cv::imwrite("DOG.jpg", dst);system("pause");return 0;
}
總結
本文介紹了圖像處理中的邊緣檢測算法,包括了Roberts、Sobel、Prewitt、laplacian、DOG、LOG等算子的C++實現和原理介紹,歡迎大家指出問題和交流討論。
參考資料:
高斯濾波(Gauss filtering)
圖像處理 | 最常用的邊緣檢測詳解與代碼(Robert, Sober, Prewitt, Canny, Kirsch, Laplacian, LOG, DOG算子)
LOG算子實現