計算機視覺中的邊緣檢測算法

摘要:?本文全面深入地探討了計算機視覺中的邊緣檢測算法。首先闡述了邊緣檢測的重要性及其在計算機視覺領域的基礎地位,隨后詳細介紹了經典的邊緣檢測算法,包括基于梯度的 Sobel 算子算法、Canny 邊緣檢測算法等,深入剖析了它們的原理、數學模型、算法步驟以及各自的優缺點。接著探討了這些算法在不同應用場景下的表現,如在圖像分析、目標識別、計算機輔助設計等領域的應用。最后,分別使用 C# 和 Python 語言實現了 Sobel 算子和 Canny 邊緣檢測算法,并對實現代碼進行了詳細的注釋和講解,通過實際代碼展示了算法的具體操作流程,旨在為計算機視覺領域的研究人員、開發者以及相關專業學生提供系統的邊緣檢測算法知識及實用的代碼參考,幫助他們更好地理解和應用邊緣檢測技術。

一、引言

在計算機視覺領域,邊緣檢測是一項極為關鍵的基礎任務。圖像中的邊緣包含了豐富的信息,它是圖像中不同區域的邊界,能夠表征物體的輪廓、形狀以及物體與背景之間的關系等重要特征。通過邊緣檢測,可以將這些有意義的邊緣信息提取出來,為后續的圖像分析、目標識別、圖像分割等高級任務提供有力的支持。例如,在自動駕駛技術中,準確的邊緣檢測能夠幫助識別道路、車輛和行人的輪廓,從而實現安全的駕駛決策;在醫學圖像處理中,邊緣檢測有助于醫生清晰地觀察病變組織的邊界,輔助疾病的診斷和治療方案的制定;在計算機輔助設計(CAD)領域,邊緣檢測可用于提取設計圖紙中的線條和輪廓,便于進行后續的模型構建和編輯。

二、邊緣檢測的基本概念

邊緣是指圖像中像素值發生急劇變化的位置,這種變化可以是灰度值的突變、顏色的差異或者紋理的改變等。邊緣檢測的目標就是找到這些像素值變化劇烈的點,并將它們連接成邊緣曲線或輪廓。從數學角度來看,邊緣通常對應著圖像函數的一階導數或二階導數的局部極值點。

三、基于梯度的邊緣檢測算法 - Sobel 算子

  1. 原理
    • Sobel 算子是一種常用的基于梯度的邊緣檢測算子。它通過計算圖像在水平和垂直方向上的灰度變化率(即梯度)來確定邊緣的位置。Sobel 算子使用兩個3x3的卷積核,一個用于檢測水平方向的邊緣,另一個用于檢測垂直方向的邊緣。
    • 水平方向的卷積核Gx為:
    • 垂直方向的卷積核Gy為:
    • 對于圖像中的每個像素(x,y),將其鄰域與這兩個卷積核分別進行卷積運算,得到水平方向的梯度值Gx(x,y)和垂直方向的梯度值Gy(x,y)。然后,根據梯度幅值公式計算該像素點的梯度幅值,梯度方向為。通常,將梯度幅值大于某個閾值的點視為邊緣點。
  2. 算法步驟
    • 讀取圖像,獲取圖像的寬度、高度和像素數據。
    • 初始化兩個與圖像大小相同的矩陣,用于存儲水平和垂直方向的梯度值。
    • 遍歷圖像中的每個像素(除了邊緣像素,因為邊緣像素的鄰域不完整)。
    • 對于每個像素,使用水平方向卷積核對其鄰域進行卷積計算,得到水平方向梯度值并存儲到對應的矩陣中。
    • 同樣,使用垂直方向卷積核對其鄰域進行卷積計算,得到垂直方向梯度值并存儲到另一個矩陣中。
    • 根據梯度幅值公式計算每個像素的梯度幅值。
    • 設定一個閾值,將梯度幅值大于閾值的像素標記為邊緣點,可以通過將這些邊緣點的像素值設置為特定值(如白色)來顯示邊緣圖像。

四、Canny 邊緣檢測算法

  1. 原理
    • Canny 邊緣檢測算法是一種較為復雜但效果優秀的邊緣檢測算法,它主要包括以下幾個步驟:
    • 噪聲平滑:首先使用高斯濾波器對圖像進行平滑處理,以去除噪聲對邊緣檢測的干擾。高斯濾波器能夠在平滑圖像的同時保留圖像的邊緣信息,其二維高斯函數為,其中為標準差,它決定了高斯濾波器的平滑程度。
    • 計算梯度幅值和方向:與 Sobel 算子類似,使用合適的卷積核(如 Sobel 卷積核)計算圖像在水平和垂直方向的梯度值,進而得到梯度幅值和方向。
    • 非極大值抑制:在得到梯度幅值圖像后,對其進行非極大值抑制。其目的是將局部范圍內梯度幅值不是最大的像素點抑制為非邊緣點。具體做法是,對于每個像素點,比較其在梯度方向上的鄰域像素的梯度幅值,如果該像素點的梯度幅值不是局部最大,則將其標記為非邊緣點,這樣可以細化邊緣,使邊緣更精確。
    • 雙閾值檢測與邊緣連接:設定兩個閾值,高閾值Th和低閾值Tl(通常Th>Tl)。首先,將梯度幅值大于高閾值的像素點確定為強邊緣點,這些點肯定是邊緣點。然后,對于梯度幅值在低閾值和高閾值之間的像素點,如果它們與強邊緣點相鄰,則將其確定為弱邊緣點并保留;否則,將其視為非邊緣點而丟棄。最后,通過邊緣連接算法將弱邊緣點與強邊緣點連接起來,形成完整的邊緣。
  2. 算法步驟
    • 讀取圖像,獲取圖像的寬度、高度和像素數據。
    • 使用高斯濾波器對圖像進行平滑處理,確定高斯核大小和標準差,計算濾波后的圖像數據。
    • 計算濾波后圖像的水平和垂直方向梯度值、梯度幅值和方向,可使用類似 Sobel 算子的計算方法。
    • 對梯度幅值圖像進行非極大值抑制,遍歷圖像中的每個像素,根據其梯度方向和鄰域像素的梯度幅值判斷是否抑制該像素。
    • 設定雙閾值Th和Tl,進行雙閾值檢測與邊緣連接。首先標記強邊緣點,然后遍歷梯度幅值在低閾值和高閾值之間的像素點,判斷其與強邊緣點的相鄰關系并確定是否保留為弱邊緣點,最后連接弱邊緣點和強邊緣點形成邊緣圖像。

五、Sobel 算子與 Canny 邊緣檢測算法的優缺點

(一)Sobel 算子

  1. 優點
    • 計算簡單,速度較快,能夠快速地檢測出圖像中的邊緣信息,對于一些簡單的圖像邊緣檢測任務具有較好的效果。
    • 可以分別得到水平和垂直方向的邊緣信息,在某些特定應用場景下(如檢測圖像中的水平或垂直線條)較為有用。
  2. 缺點
    • 對噪聲比較敏感,由于沒有專門的噪聲平滑步驟,在噪聲較多的圖像中可能會檢測出大量的偽邊緣,導致邊緣檢測結果不準確。
    • 邊緣檢測的精度相對較低,得到的邊緣較粗,可能無法準確地描繪出物體的精細輪廓。

(二)Canny 邊緣檢測算法

  1. 優點
    • 檢測精度高,通過非極大值抑制和雙閾值檢測等步驟能夠得到較為精確和連續的邊緣,對圖像中的弱邊緣也有較好的檢測能力,能夠更準確地描繪物體的輪廓。
    • 對噪聲具有一定的魯棒性,因為在算法開始階段使用了高斯濾波器進行噪聲平滑,減少了噪聲對邊緣檢測的影響。
  2. 缺點
    • 算法相對復雜,計算量較大,尤其是在非極大值抑制和邊緣連接步驟中需要對圖像中的每個像素進行多次比較和判斷,導致處理速度較慢,在實時性要求較高的應用場景中可能不太適用。

六、Sobel 算子的 C# 實現

以下是使用 C# 實現 Sobel 算子邊緣檢測的代碼示例:

using System;
using System.Drawing;
using System.Drawing.Imaging;class SobelOperator
{// 計算水平方向梯度private static void SobelHorizontalGradient(Bitmap sourceImage, int[,] horizontalGradient){int width = sourceImage.Width;int height = sourceImage.Height;BitmapData sourceData = sourceImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);unsafe{byte* sourcePtr = (byte*)sourceData.Scan0.ToPointer();int[,] sobelX = { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } };for (int y = 1; y < height - 1; y++){for (int x = 1; x < width - 1; x++){int sumX = 0;for (int i = -1; i <= 1; i++){for (int j = -1; j <= 1; j++){int xIndex = x + j;int yIndex = y + i;int sourceIndex = (yIndex * sourceData.Stride) + (xIndex * 3);sumX += sobelX[i + 1, j + 1] * sourcePtr[sourceIndex];}}horizontalGradient[y, x] = sumX;}}}sourceImage.UnlockBits(sourceData);}// 計算垂直方向梯度private static void SobelVerticalGradient(Bitmap sourceImage, int[,] verticalGradient){int width = sourceImage.Width;int height = sourceImage.Height;BitmapData sourceData = sourceImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);unsafe{byte* sourcePtr = (byte*)sourceData.Scan0.ToPointer();int[,] sobelY = { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } };for (int y = 1; y < height - 1; y++){for (int x = 1; x < width - 1; x++){int sumY = 0;for (int i = -1; i <= 1; i++){for (int j = -1; j <= 1; j++){int xIndex = x + j;int yIndex = y + i;int sourceIndex = (yIndex * sourceData.Stride) + (xIndex * 3);sumY += sobelY[i + 1, j + 1] * sourcePtr[sourceIndex + 1];}}verticalGradient[y, x] = sumY;}}}sourceImage.UnlockBits(sourceData);}// 計算梯度幅值private static void GradientMagnitude(int[,] horizontalGradient, int[,] verticalGradient, Bitmap outputImage){int width = outputImage.Width;int height = outputImage.Height;BitmapData outputData = outputImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);unsafe{byte* outputPtr = (byte*)outputData.Scan0.ToPointer();for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){int gx = horizontalGradient[y, x];int gy = verticalGradient[y, x];double magnitude = Math.Sqrt(gx * gx + gy * gy);// 歸一化梯度幅值到0-255范圍int value = (int)(magnitude * 255.0 / Math.Sqrt(2 * 255 * 255));value = Math.Min(255, Math.Max(0, value));int outputIndex = (y * outputData.Stride) + (x * 3);outputPtr[outputIndex] = (byte)value;outputPtr[outputIndex + 1] = (byte)value;outputPtr[outputIndex + 2] = (byte)value;}}}outputImage.UnlockBits(outputData);}// Sobel算子邊緣檢測主函數public static Bitmap SobelEdgeDetection(Bitmap sourceImage){int width = sourceImage.Width;int height = sourceImage.Height;int[,] horizontalGradient = new int[height, width];int[,] verticalGradient = new int[height, width];// 計算水平和垂直方向梯度SobelHorizontalGradient(sourceImage, horizontalGradient);SobelVerticalGradient(sourceImage, verticalGradient);// 創建輸出圖像Bitmap outputImage = new Bitmap(width, height);// 計算梯度幅值并生成邊緣圖像GradientMagnitude(horizontalGradient, verticalGradient, outputImage);return outputImage;}
}

在上述代碼中,SobelHorizontalGradient方法使用水平方向的 Sobel 卷積核計算圖像的水平方向梯度值并存儲在horizontalGradient矩陣中。SobelVerticalGradient方法類似地計算垂直方向梯度值并存儲在verticalGradient矩陣中。GradientMagnitude方法根據水平和垂直方向梯度值計算梯度幅值,并將其歸一化后設置到輸出圖像的像素值中,最后SobelEdgeDetection方法作為主函數,調用前面的方法完成整個 Sobel 算子邊緣檢測過程并返回邊緣圖像。

七、Canny 邊緣檢測算法的 C# 實現

以下是使用 C# 實現 Canny 邊緣檢測算法的代碼示例:

using System;
using System.Drawing;
using System.Drawing.Imaging;class CannyEdgeDetector
{// 高斯濾波函數private static void GaussianFilter(Bitmap sourceImage, double sigma, Bitmap outputImage){int width = sourceImage.Width;int height = sourceImage.Height;BitmapData sourceData = sourceImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);BitmapData outputData = outputImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);double[,] kernel = GenerateGaussianKernel(3, sigma);int center = 1;unsafe{byte* sourcePtr = (byte*)sourceData.Scan0.ToPointer();byte* outputPtr = (byte*)outputData.Scan0.ToPointer();for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){double red = 0, green = 0, blue = 0;for (int i = -center; i <= center; i++){for (int j = -center; j <= center; j++){int xIndex = Math.Max(0, Math.Min(x + j, width - 1));int yIndex = Math.Max(0, Math.Min(y + i, height - 1));int sourceIndex = (yIndex * sourceData.Stride) + (xIndex * 3);red += kernel[i + center, j + center] * sourcePtr[sourceIndex];green += kernel[i + center, j + center] * sourcePtr[sourceIndex + 1];blue += kernel[i + center, j + center] * sourcePtr[sourceIndex + 2];}}int outputIndex = (y * outputData.Stride) + (x * 3);outputPtr[outputIndex] = (byte)Math.Min(255, Math.Max(0, red));outputPtr[outputIndex + 1] = (byte)Math.Min(255, Math.Max(0, green));outputPtr[outputIndex + 2] = (byte)Math.Min(255, Math.Max(0, blue));}}}sourceImage.UnlockBits(sourceData);outputImage.UnlockBits(outputData);}// 計算梯度幅值和方向private static void Gradient(Bitmap sourceImage, int[,] gradientMagnitude, double[,] gradientDirection){int width = sourceImage.Width;int height = sourceImage.Height;BitmapData sourceData = sourceImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);unsafe{byte* sourcePtr = (byte*)sourceData.Scan0.ToPointer();int[,] sobelX = { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } };int[,] sobelY = { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } };for (int y = 1; y < height - 1; y++){for (int x = 1; x < width - 1; x++){int gx = 0, gy = 0;for (int i = -1; i <= 1; i++){for (int j = -1; j <= 1; j++){int xIndex = x + j;int yIndex = y + i;int sourceIndex = (yIndex * sourceData.Stride) + (xIndex * 3);gx += sobelX[i + 1, j + 1] * sourcePtr[sourceIndex];gy += sobelY[i + 1, j + 1] * sourcePtr[sourceIndex + 1];}}gradientMagnitude[y, x] = (int)Math.Sqrt(gx * gx + gy * gy);gradientDirection[y, x] = Math.Atan2(gy, gx);}}}sourceImage.UnlockBits(sourceData);}// 非極大值抑制private static void NonMaxSuppression(int[,] gradientMagnitude, double[,] gradientDirection, int[,] nmsOutput){int width = gradientMagnitude.GetLength(1);int height = gradientMagnitude.GetLength(0);for (int y = 1; y < height - 1; y++){for (int x = 1; x < width - 1; x++){double angle = gradientDirection[y, x];if ((angle >= -Math.PI / 8 && angle < Math.PI / 8) || (angle >= 7 * Math.PI / 8 || angle < -7 * Math.PI / 8)){// 水平方向比較if (gradientMagnitude[y, x] <= gradientMagnitude[y, x - 1] || gradientMagnitude[y, x] <= gradientMagnitude[y, x + 1]){nmsOutput[y, x] = 0;}else{nmsOutput[y, x] = gradientMagnitude[y, x];}}else if ((angle >= Math.PI / 8 && angle < 3 * Math.PI / 8) || (angle >= -7 * Math.PI / 8 && angle < -5 * Math.PI / 8)){// 對角線方向(右上 - 左下)比較if (gradientMagnitude[y, x] <= gradientMagnitude[y - 1, x + 1] || gradientMagnitude[y, x] <= gradientMagnitude[y + 1, x - 1]){nmsOutput[y, x] = 0;}else{nmsOutput[y, x] = gradientMagnitude[y, x];}}else if ((angle >= 3 * Math.PI / 8 && angle < 5 * Math.PI / 8) || (angle >= -5 * Math.PI / 8 && angle < -3 * Math.PI / 8)){// 垂直方向比較if (gradientMagnitude[y, x] <= gradientMagnitude[y - 1, x] || gradientMagnitude[y, x] <= gradientMagnitude[y + 1, x]){nmsOutput[y, x] = 0;}else{nmsOutput[y, x] = gradientMagnitude[y, x];}}else if ((angle >= 5 * Math.PI / 8 && angle < 7 * Math.PI / 8) || (angle >= -3 * Math.PI / 8 && angle < -Math.PI / 8)){// 對角線方向(左上 - 右下)比較if (gradientMagnitude[y, x] <= gradientMagnitude[y - 1, x - 1] || gradientMagnitude[y, x] <= gradientMagnitude[y + 1, x + 1]){nmsOutput[y, x] = 0;}else{nmsOutput[y, x] = gradientMagnitude[y, x];}}}}}// 雙閾值檢測與邊緣連接private static void Hysteresis(int[,] nmsOutput, int lowThreshold, int highThreshold, Bitmap outputImage){int width = nmsOutput.GetLength(1);int height = nmsOutput.GetLength(0);BitmapData outputData = outputImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);unsafe{byte* outputPtr = (byte*)outputData.Scan0.ToPointer();for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){if (nmsOutput[y, x] >= highThreshold){outputPtr[(y * outputData.Stride) + (x * 3)] = 255;outputPtr[(y * outputData.Stride) + (x * 3) + 1] = 255;outputPtr[(y * outputData.Stride) + (x * 3) + 2] = 255;}else if (nmsOutput[y, x] >= lowThreshold){// 檢查鄰域像素是否有強邊緣點bool hasStrongNeighbor = false;for (int i = -1; i <= 1; i++){for (int j = -1; j <= 1; j++){int xIndex = Math.Max(0, Math.Min(x + j, width - 1));int yIndex = Math.Max(0, Math.Min(y + i, height - 1));if (nmsOutput[yIndex, xIndex] >= highThreshold){hasStrongNeighbor = true;break;}}if (hasStrongNeighbor){break;}}if (hasStrongNeighbor){outputPtr[(y * outputData.Stride) + (x * 3)] = 255;outputPtr[(y * outputData.Stride) + (x * 3) + 1] = 255;outputPtr[(y * outputData.Stride) + (x * 3) + 2] = 255;}else{outputPtr[(y * outputData.Stride) + (x * 3)] = 0;outputPtr[(y * outputData.Stride) + (x * 3) + 1] = 0;outputPtr[(y * outputData.Stride) + (x * 3) + 2] = 0;}}else{outputPtr[(y * outputData.Stride) + (x * 3)] = 0;outputPtr[(y * outputData.Stride) + (x * 3) + 1] = 0;outputPtr[(y * outputData.Stride) + (x * 3) + 2] = 0;}}}}outputImage.UnlockBits(outputData);}// 生成高斯核private static double[,] GenerateGaussianKernel(int size, double sigma){double[,] kernel = new double[size, size];int center = size / 2;double sum = 0;for (int i = 0; i < size; i++){for (int j = 0; j < size; j++){int x = i - center;int y = j - center;kernel[i, j] = (1.0 / (2 * Math.PI * sigma * sigma)) * Math.Exp(-(x * x + y * y) / (2 * sigma * sigma));sum += kernel[i, j];}}// 歸一化高斯核for (int i = 0; i < size; i++){for (int j = 0; j < size; j++){kernel[i, j] /= sum;}}return kernel;}// Canny邊緣檢測主函數public static Bitmap CannyEdgeDetection(Bitmap sourceImage, double sigma, int lowThreshold, int highThreshold){// 高斯濾波Bitmap filteredImage = new Bitmap(sourceImage.Width, sourceImage.Height);GaussianFilter(sourceImage, sigma, filteredImage);// 計算梯度幅值和方向int[,] gradientMagnitude = new int[filteredImage.Height, filteredImage.Width];double[,] gradientDirection = new double[filteredImage.Height, filteredImage.Width];Gradient(filteredImage, gradientMagnitude, gradientDirection);// 非極大值抑制int[,] nmsOutput = new int[filteredImage.Height, filteredImage.Width];NonMaxSuppression(gradientMagnitude, gradientDirection, nmsOutput);// 雙閾值檢測與邊緣連接Bitmap outputImage = new Bitmap(filteredImage.Width, filteredImage.Height);Hysteresis(nmsOutput, lowThreshold, highThreshold, outputImage);return outputImage;}
}

在上述 C# 代碼中,GaussianFilter方法實現了高斯濾波功能,對輸入圖像進行平滑處理。Gradient方法使用 Sobel 算子計算濾波后圖像的梯度幅值和方向。NonMaxSuppression方法執行非極大值抑制操作,細化邊緣。Hysteresis方法進行雙閾值檢測與邊緣連接,確定最終的邊緣圖像。GenerateGaussianKernel方法用于生成高斯核。CannyEdgeDetection作為主函數,依次調用上述方法完成整個 Canny 邊緣檢測過程并返回邊緣圖像。

八、Sobel 算子的 Python 實現

import cv2
import numpy as npdef sobel_edge_detection(image):"""使用Sobel算子進行邊緣檢測:param image: 輸入圖像:return: 邊緣檢測后的圖像"""# 獲取圖像的高度、寬度和通道數height, width, channels = image.shape# 轉換為灰度圖像(Sobel算子通常在灰度圖像上操作)gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 計算水平方向梯度sobel_x = cv2.Sobel(gray_image, cv2.CV_64F, 1, 0, ksize=3)# 計算垂直方向梯度sobel_y = cv2.Sobel(gray_image, cv2.CV_64F, 0, 1, ksize=3)# 計算梯度幅值gradient_magnitude = np.sqrt(sobel_x ** 2 + sobel_y ** 2)# 歸一化梯度幅值到0-255范圍gradient_magnitude = (gradient_magnitude / np.max(gradient_magnitude)) * 255# 將梯度幅值轉換為無符號8位整數類型gradient_magnitude = gradient_magnitude.astype(np.uint8)return gradient_magnitude

在這個 Python 代碼中,首先使用cv2.cvtColor將輸入圖像轉換為灰度圖像,因為 Sobel 算子通常在灰度圖像上進行邊緣檢測。然后使用cv2.Sobel函數分別計算水平和垂直方向的梯度,其中cv2.CV_64F表示輸出數據類型為 64 位浮點數,1, 0表示計算水平方向梯度(dx = 1, dy = 0),0, 1表示計算垂直方向梯度(dx = 0, dy = 1),ksize = 3表示使用的 Sobel 卷積核。接著計算梯度幅值,并進行歸一化和數據類型轉換,最后返回邊緣檢測后的圖像。

九、Canny 邊緣檢測算法的 Python 實現

import cv2
import numpy as npdef canny_edge_detection(image, sigma=1.4, low_threshold=50, high_threshold=150):"""使用Canny邊緣檢測算法進行邊緣檢測:param image: 輸入圖像:param sigma: 高斯濾波標準差:param low_threshold: 低閾值:param high_threshold: 高閾值:return: 邊緣檢測后的圖像"""# 高斯濾波blurred_image = cv2.GaussianBlur(image, (3, 3), sigma)# 轉換為灰度圖像gray_image = cv2.cvtColor(blurred_image, cv2.COLOR_BGR2GRAY)# 計算梯度幅值和方向gradient_magnitude, gradient_direction = cv2.Canny(gray_image, low_threshold, high_threshold, L2gradient=True)return gradient_magnitude

在上述 Python 代碼中,cv2.GaussianBlur函數用于對輸入圖像進行高斯濾波,(3, 3)表示高斯核大小為3x3。然后將濾波后的圖像轉換為灰度圖像,再使用cv2.Canny函數進行 Canny 邊緣檢測,其中L2gradient=True表示使用精確的梯度幅值計算(即使用歐幾里得距離計算梯度幅值),函數返回計算得到的梯度幅值圖像,即邊緣檢測后的圖像。通過這些 Python 代碼實現,可以方便地在 Python 環境中應用 Sobel 算子和 Canny 邊緣檢測算法進行圖像邊緣檢測任務。

十、總結

邊緣檢測算法在計算機視覺領域具有舉足輕重的地位。Sobel 算子以其簡單快速的特點,在一些對邊緣檢測精度要求不高且實時性較強的場景中有著廣泛的應用,如簡單的圖像預處理、實時監控中的初步輪廓提取等。然而,對于復雜圖像和高精度需求場景,Canny 邊緣檢測算法憑借其出色的檢測精度和對噪聲的魯棒性脫穎而出。它在醫學影像分析、工業零件檢測等領域能夠精準地勾勒出目標邊緣,為后續的診斷、測量等工作提供可靠依據。無論是 C# 還是 Python 語言的實現,都為開發者提供了便利的工具,使其能夠根據項目的具體需求和運行環境靈活選擇合適的語言來部署邊緣檢測算法,從而推動計算機視覺技術在眾多領域的深入發展與廣泛應用,不斷提升圖像處理和分析的效率與準確性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/63097.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/63097.shtml
英文地址,請注明出處:http://en.pswp.cn/web/63097.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Unix 和 Windows 的有趣比較

Unix 和 Windows NT 比較 來源于這兩本書&#xff0c;把兩本書對照來讀&#xff0c;發現很多有意思的地方&#xff1a; 《Unix 傳奇》 https://book.douban.com/subject/35292726/ 《觀止 微軟創建NT和未來的奪命狂奔 》 Showstopper!: The Breakneck Race to Create Windows…

SSM 垃圾分類系統——高效分類的科技保障

第五章 系統功能實現 5.1管理員登錄 管理員登錄&#xff0c;通過填寫用戶名、密碼、角色等信息&#xff0c;輸入完成后選擇登錄即可進入垃圾分類系統&#xff0c;如圖5-1所示。 圖5-1管理員登錄界面圖 5.2管理員功能實現 5.2.1 用戶管理 管理員對用戶管理進行填寫賬號、姓名、…

系列1:基于Centos-8.6部署Kubernetes (1.24-1.30)

每日禪語 “木末芙蓉花&#xff0c;山中發紅萼&#xff0c;澗戶寂無人&#xff0c;紛紛開自落。?”這是王維的一首詩&#xff0c;名叫《辛夷塢》?。這首詩寫的是在辛夷塢這個幽深的山谷里&#xff0c;辛夷花自開自落&#xff0c;平淡得很&#xff0c;既沒有生的喜悅&#xff…

Y20030004基于asp.net+Sql的環保網站的設計與實現(附源碼 調試 文檔)

環保網站的設計與實現 1.摘要要2. 系統功能3.功能結構圖4.界面展示5.源碼獲取 1.摘要要 近幾年國家對于環境管理是高度重視&#xff0c;尤其是對于環境生態的破壞與環境污染&#xff0c;已經嚴重影響到人類的生存和發展。為了使生態環境能夠得到保護和改善&#xff0c;持續發展…

安全計算環境-(一)路由器-1

安全計算環境-網絡設備 安全管理中心針對整個系統提出了安全管理方面的技術控制要求&#xff0c;通過技術手段實現集中管理&#xff1b;涉及的安全控制點包括系統管理、審計管理、安全管理和集中管控。以下以三級等級保護對象為例&#xff0c;描述安全管理中心各個控制要求項的…

D9741是一塊脈寬調制方三用于也收路像機和筆記本電的等設備上的直流轉換器。在便攜式的儀器設備上。

概述&#xff1a; D9741是一塊脈寬調制方三用于也收路像機和筆記本電的等設備上的直流轉換器。在便攜式的儀器設備上。 主要特點&#xff1a; ● 高精度基準電路 ● 定時閂鎖、短路保護電路 ● 低電壓輸入時誤操作保護電路 ● 輸出基準電壓(2.5V) ● 超過工作范圍能進行自動校…

數據挖掘之聚類分析

聚類分析&#xff08;Clustering Analysis&#xff09; 是數據挖掘中的一項重要技術&#xff0c;旨在根據對象間的相似性或差異性&#xff0c;將對象分為若干組&#xff08;簇&#xff09;。同一簇內的對象相似性較高&#xff0c;而不同簇間的對象差異性較大。聚類分析廣泛應用…

Qt 圖形框架下圖形拖動后位置跳動問題

在使用Qt 的圖形框架QGraphicsScene&#xff0c;QGraphicsView實現圖形顯示時。遇到一個很棘手的BUG。 使用的圖形是自定義的QGraphicsObject的子類。 現象是將圖形添加到畫布上之后&#xff0c;用鼠標拖動圖形&#xff0c;圖形能正常改變位置&#xff0c;當再次用鼠標點擊圖…

Vue技術中參數傳遞:Props與事件的實踐指南

在Vue.js中&#xff0c;組件間的參數傳遞是構建動態和交互式應用的核心。本文將深入探討如何通過Props和事件&#xff08;$emit&#xff09;在Vue組件間進行參數傳遞&#xff0c;并提供代碼示例。 Props傳遞數據 Props是Vue中組件間傳遞數據的一種方式&#xff0c;它允許父組…

一、LRU緩存

LRU緩存 1.LRU緩存介紹2.LRU緩存實現3.LRU緩存總結3.1 LRU 緩存的應用3.2 LRU 緩存的優缺點 1.LRU緩存介紹 LRU是Least Recently Used 的縮寫&#xff0c;意為“最近最少使用”。它是一種常見的緩存淘汰策略&#xff0c;用于在緩存容量有限時&#xff0c;決定哪些數據需要被刪…

LabVIEW光柵衍射虛擬仿真系統

隨著現代教育技術的快速發展&#xff0c;虛擬仿真實驗平臺逐漸成為物理實驗教學的重要輔助工具。基于LabVIEW的平面透射光柵虛擬仿真系統幫助學生更好地理解和分析光柵衍射現象&#xff0c;提高教學質量和學生的學習興趣。 項目背景 在波動光學的教學中&#xff0c;光柵衍射實…

241211 selenium問題記錄

The process started from chrome location /usr/bin/chromedriver is no longer running, so ChromeDriver is assuming that Chrome has crashed. 聲明option類 chrome_option.add_argument(--headless) 后臺啟動webdriver NoSuchDriverException(msg) from err selenium.c…

前端核心知識總結

?前端架構知識總結?主要包括以下幾個方面&#xff1a; ?HTML?&#xff1a;HTML是構建網頁的基礎&#xff0c;使用各種標簽定義網頁的結構&#xff0c;如<html>、<head>、<body>等。HTML5引入了新的語義化標簽&#xff0c;如<article>、<section…

libcublas.so.11: cannot open shared object file: no such file or di

問題&#xff1a;在linux系統安裝tensorrt后import tensorrt時出現 libcublas.so.11: cannot open shared object file: no such file or directory 或者 libcublasLt.so.11&#xff1a;cannot open shared object file: no such file or directory 或者 libcudnn.so.8&…

Linux編譯Kernel時的文件zImage、文件dtb(dtbs)、核心模塊分別是什么東西?

zImage文件的介紹 在編譯Linux內核時&#xff0c;zImage 是一種內核映像文件&#xff0c;它是內核的壓縮版本&#xff0c;通常用于引導嵌入式設備或其他資源有限的環境。 zImage 的具體含義 zImage 是 “Compressed Kernel Image” 的縮寫。它是通過壓縮原始的內核映像&…

Spring Boot 3.x嵌入MongoDB 進行測試

在現代應用開發中&#xff0c;數據庫是不可或缺的一部分。對于使用 MongoDB 的 Java 應用&#xff0c;進行單元測試時&#xff0c;通常需要一個輕量級的數據庫實例。de.flapdoodle.embed.mongo 是一個非常有用的庫&#xff0c;它允許開發者在測試中嵌入 MongoDB 實例&#xff0…

scala隱式類

1 定義 隱式類指的是用implicit關鍵字修飾的類。在對應的作用域內&#xff0c;帶有這個關鍵字的類的主構造函數可用于隱式轉換。 2示例 現在有一個需求&#xff1a;有一個 Person 類&#xff0c;含有work&#xff08;&#xff09;方法&#xff0c;有一個 Student 類&#xff0…

Swin Transformer:用Transformer實現CNN多尺度操作

文本是關于Swin Transformer基礎知識的了解 論文&#xff1a;https://arxiv.org/pdf/2103.14030 項目&#xff1a;https://github. com/microsoft/Swin-Transformer. 實現一個Swin Transformer&#xff1a;Swin Transformer模型具體代碼實現-CSDN博客 Swin Transformer mlp…

系列2:基于Centos-8.6Kubernetes 集成GPU資源信息

每日禪語 自省&#xff0c;就是自我反省、自我檢查&#xff0c;自知己短&#xff0c;從而彌補短處、糾正過失。佛陀強調自覺覺他&#xff0c;強調以達到覺行圓滿為修行的最高境界。要改正錯誤&#xff0c;除了虛心接受他人意見之外&#xff0c;還要不忘時時觀照己身。自省自悟之…

flutter控件buildDragTargetWidget詳解

文章目錄 1. DragTarget 的核心概念基本屬性 2. 基本用法3. 使用 buildDragTargetWidget4. 常見場景5. 注意事項 buildDragTargetWidget 不是 Flutter 中的內置 API 或方法&#xff0c;但根據命名習慣&#xff0c;它很可能是您正在實現或使用的一個方法&#xff0c;用于在 Flut…