1、前言
邊緣檢測:
圖像邊緣檢測是指在圖像中尋找灰度、顏色、紋理等變化比較劇烈的區(qū)域,它們可能代表著物體之間的邊界或物體內(nèi)部的特征。邊緣檢測是圖像處理中的一項(xiàng)基本操作,可以用于人臉識別、物體識別、圖像分割等多個領(lǐng)域。
邊緣檢測實(shí)質(zhì)上是計算當(dāng)前點(diǎn)和周圍點(diǎn)灰度的差別。
圖像邊緣檢測流程主要分為以下幾個步驟:
(1)讀取待處理圖像;
(1)圖像濾波,例如使用高斯濾波器,平滑圖像,去除噪聲;
(2)計算圖像中每個像素點(diǎn)的梯度強(qiáng)度和方向;
(3)應(yīng)用非極大值抑制(Non-Maximum Suooression),保留梯度方向上的局部最大值,抑制非邊緣點(diǎn),消除邊緣檢測帶來的雜散響應(yīng);
(4)應(yīng)用雙閾值(Double-Threshold)檢測來確定真實(shí)的和潛在的邊緣,即將梯度幅值映射到兩個閾值,根據(jù)梯度值高于高閾值或在高低閾值之間的情況,將像素標(biāo)記為強(qiáng)邊緣、弱邊緣或非邊緣。
(5)邊緣連接,通過連接相鄰的強(qiáng)邊緣像素和與之相連的弱邊緣像素,形成最終的邊緣圖像;
(6)顯示結(jié)果。
在介紹各種邊緣檢測算子之前先簡單闡述下怎么尋找邊緣。
下面左圖是一張黑白相間的圖,右圖是左圖的每個像素的灰度值
我們設(shè)定一個卷積核如下(關(guān)于卷積的介紹請看之前的文章):
原圖在通過卷積核進(jìn)行卷積計算后得到的圖像如下:
可以看到原圖在卷積運(yùn)算后黑色向白色突變的邊緣被很好的保留了下來,因此可以通過這個卷積核找到圖像中垂直的邊緣。
同理,如果我們用下面的卷積核也可以找到圖像中水平的邊緣。
卷積運(yùn)算后:
2、Prewitt算子邊緣檢測
如果我們把上面兩個卷積核組合起來再對圖像進(jìn)行卷積便可以同時找到圖像中水平和垂直的邊緣,這種卷積核就是prewitt算子。
標(biāo)準(zhǔn)的 Prewitt 邊緣檢測算子由以下兩個卷積核組成。
下面是用prewitt算子進(jìn)行邊緣檢測的案例:
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
int main() {
// 讀取圖像
Mat image = imread("your_image.jpg", IMREAD_GRAYSCALE);
// 定義Prewitt算子
Mat prewitt_x = (Mat_<float>(3, 3) << -1, 0, 1, -1, 0, 1, -1, 0, 1);
Mat prewitt_y = (Mat_<float>(3, 3) << -1, -1, -1, 0, 0, 0, 1, 1, 1);
// 對圖像應(yīng)用Prewitt算子
Mat edges_x, edges_y;
filter2D(image, edges_x, CV_64F, prewitt_x);
filter2D(image, edges_y, CV_64F, prewitt_y);
// 計算梯度幅值和方向
Mat gradient_magnitude, gradient_direction;
magnitude(edges_x, edges_y, gradient_magnitude);
phase(edges_x, edges_y, gradient_direction, true);
// 歸一化梯度幅值
cv::normalize(gradient_magnitude, gradient_magnitude, 0, 1, cv::NORM_MINMAX);
// 顯示結(jié)果
imshow("Original Image", image); //原灰度圖
imshow("Gradient Magnitude", gradient_magnitude); //prewitt算子邊緣檢測圖
waitKey(0);
destroyAllWindows();
return 0;
}
代碼解讀:
(1)在代碼中我們先分別定義一個水平方向和垂直方向的prewitt算子edges_x和edges_y
(2)filter2D是對圖像進(jìn)行卷積操作,即獲取prewitt算子與原圖像卷積后的圖像edges_x和edges_y
(3)magnitude 函數(shù)的主要用途是計算兩個輸入數(shù)組的逐元素平方和的平方根。在圖像處理中,常常用于計算圖像中每個像素點(diǎn)的梯度幅值。相位(Phase)在圖像處理中通常指的是梯度的方向(邊緣方向)。在梯度計算中,梯度向量的方向表示圖像在該點(diǎn)上灰度變化最快的方向。在梯度計算中,通常使用 magnitude 函數(shù)計算梯度的幅值,使用 phase 函數(shù)計算梯度的方向。這兩個信息一起構(gòu)成了梯度向量,提供了有關(guān)圖像局部變化的重要信息。
(4)最后歸一化梯度幅值圖像,因?yàn)?4位圖像顯示范圍為0-1。
最后效果如下(左邊是原灰度圖,右邊是邊緣檢測出的圖像):
3、Roberts算子
常用來處理具有陡峭的低噪聲圖像,當(dāng)圖像邊緣接近于正45度或負(fù)45度時,該算法處理效果更理想。其缺點(diǎn)是對邊緣的定位不太準(zhǔn)確,提取的邊緣線條較粗。
下圖左邊為水平方向Roberts算子,也稱正對角算子。右邊為垂直方向Roberts算子,也稱斜對角算子。
下面是Roberts算子的使用案例:
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
int main() {
// 讀取圖像
Mat image = imread("1.jpg", IMREAD_GRAYSCALE);
if (image.empty()) {
std::cerr << "Error: Could not read the image." << std::endl;
return -1;
}
// 初始化 Roberts 算子
Mat robertsX = (Mat_<float>(2, 2) << -1, 0, 0, 1); //這邊要寫成float類型,不能是Int
Mat robertsY = (Mat_<float>(2, 2) << 0, -1, 1, 0);
// 應(yīng)用 Roberts 算子進(jìn)行邊緣檢測
Mat gradientX, gradientY;
filter2D(image, gradientX, CV_64F, robertsX); //應(yīng)用CV_64F,不能用默認(rèn)-1,不然magnitude會拋出異常
filter2D(image, gradientY, CV_64F, robertsY);
// 計算梯度幅值
Mat gradientMagnitude, gradient_direction;
magnitude(gradientX, gradientY, gradientMagnitude);
cv::phase(gradientX, gradientY, gradient_direction, true); // true 表示計算角度,false計算弧度
// 歸一化梯度方向到[0, 1]范圍,因?yàn)?4位顯示范圍為0-1
cv::normalize(gradientMagnitude, gradientMagnitude, 0, 1, cv::NORM_MINMAX);
// 顯示原始圖像和邊緣檢測結(jié)果
namedWindow("Original Image", WINDOW_NORMAL);
namedWindow("Roberts Edge Detection", WINDOW_NORMAL);
imshow("Original Image", image);
imshow("Roberts Edge Detection", gradientMagnitude);
waitKey(0);
destroyAllWindows();
return 0;
}
最后效果如下(左邊是原灰度圖,右邊是邊緣檢測出的圖像):
4、Sobel算子邊緣檢測
Sobel算子在Prewitt算子的基礎(chǔ)上增加了權(quán)重的概念,認(rèn)為相鄰點(diǎn)的距離遠(yuǎn)近對當(dāng)前像素點(diǎn)的影響是不同的,距離越近的像素點(diǎn)對應(yīng)當(dāng)前像素的影響越大,從而實(shí)現(xiàn)圖像銳化并突出邊緣輪廓。但Sobel算子并不是基于圖像灰度進(jìn)行處理的,因?yàn)镾obel算子并沒有嚴(yán)格地模擬人的視覺生理特性,因此圖像輪廓的提取有時并不能讓人滿意。當(dāng)對精度要求不是很高時,Sobel算子是一種較為常用的邊緣檢測方法。
它的水平和垂直方向的卷積核如下:
接口說明:
void cv::Sobel(
InputArray src,
OutputArray dst,
int ddepth,
int dx,
int dy,
int ksize = 3,
double scale = 1,
double delta = 0,
int borderType = cv::BORDER_DEFAULT
);
src: 輸入圖像。可以是單通道(灰度圖)或多通道圖像。
dst: 輸出圖像,梯度的計算結(jié)果將存儲在這里。
ddepth: 輸出圖像的深度,通常使用 CV_64F 或 CV_32F 表示。
dx: x方向上的導(dǎo)數(shù)階數(shù),設(shè)為1表示在水平方向上進(jìn)行操作。
dy: y方向上的導(dǎo)數(shù)階數(shù),設(shè)為1表示在垂直方向上進(jìn)行操作。
ksize: Sobel核的大小。默認(rèn)為 3,表示一個 3x3 的核。通常使用奇數(shù)值。
scale: 可選的比例因子,用于調(diào)整梯度的幅值,也表示對比度。
delta: 可選的偏移值,用于調(diào)整輸出圖像的亮度。
borderType: 邊界處理類型,可以使用 cv::BORDER_DEFAULT 或其他合適的邊界處理標(biāo)志。
Sobel算子邊緣檢測案例:文章來源:http://www.zghlxwxcb.cn/news/detail-836362.html
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
int main() {
// 讀取圖像
cv::Mat image = cv::imread("1.jpg", cv::IMREAD_GRAYSCALE);
if (image.empty()) {
std::cerr << "Error: Could not read the image." << std::endl;
return -1;
}
// 應(yīng)用Sobel算子
cv::Mat sobelX, sobelY;
cv::Sobel(image, sobelX, CV_64F, 1, 0, 3); // 1表示在水平方向上進(jìn)行操作
cv::Sobel(image, sobelY, CV_64F, 0, 1, 3); // 1表示在垂直方向上進(jìn)行操作
// 轉(zhuǎn)換為絕對值,防止負(fù)值影響計算
sobelX = cv::abs(sobelX);
sobelY = cv::abs(sobelY);
// 合并水平和垂直梯度
cv::Mat sobelResult;
cv::addWeighted(sobelX, 0.5, sobelY, 0.5, 0, sobelResult);
// 將16位深度圖像轉(zhuǎn)換為8位深度,以便顯示
cv::Mat sobel8U;
sobelResult.convertTo(sobel8U, CV_8U);
// 顯示原始圖像和Sobel邊緣檢測結(jié)果
cv::imshow("Original Image", image);
cv::imshow("Sobel Edge Detection", sobel8U);
cv::waitKey(0);
return 0;
}
文章來源地址http://www.zghlxwxcb.cn/news/detail-836362.html
到了這里,關(guān)于《opencv實(shí)用探索·十一》opencv之Prewitt算子邊緣檢測,Roberts算子邊緣檢測和Sobel算子邊緣檢測的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!