当前位置: 首页 > news >正文

计算机视觉中的边缘检测算法

摘要: 本文全面深入地探讨了计算机视觉中的边缘检测算法。首先阐述了边缘检测的重要性及其在计算机视觉领域的基础地位,随后详细介绍了经典的边缘检测算法,包括基于梯度的 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 语言的实现,都为开发者提供了便利的工具,使其能够根据项目的具体需求和运行环境灵活选择合适的语言来部署边缘检测算法,从而推动计算机视觉技术在众多领域的深入发展与广泛应用,不断提升图像处理和分析的效率与准确性。

相关文章:

计算机视觉中的边缘检测算法

摘要&#xff1a; 本文全面深入地探讨了计算机视觉中的边缘检测算法。首先阐述了边缘检测的重要性及其在计算机视觉领域的基础地位&#xff0c;随后详细介绍了经典的边缘检测算法&#xff0c;包括基于梯度的 Sobel 算子算法、Canny 边缘检测算法等&#xff0c;深入剖析了它们的…...

js 常用扩展方法总结+应用

文章目录 js 常用扩展方法总结扩展方法应用选择大型项目 中扩展方法应用选择小型项目中 扩展应用 js 常用扩展方法总结 函数原型&#xff08;prototype&#xff09;扩展方法 介绍&#xff1a;在JavaScript中&#xff0c;通过修改函数的prototype属性可以为该函数创建的所有对象…...

数据结构---图(Graph)

图&#xff08;Graph&#xff09;是一种非常灵活且强大的数据结构&#xff0c;用于表示实体之间的复杂关系。在图结构中&#xff0c;数据由一组节点&#xff08;或称为顶点&#xff09;和连接这些节点的边组成。图可以用于表示社交网络、交通网络、网络路由等场景。 1. 基本概…...

前端解析超图的iserver xml

前端解析超图的iserver xml const res await axios.get(url)const xmlDom new DOMParser().parseFromString(res.data, text/xml);// 获取versionconst version xmlDom.getElementsByTagNameNS(*, ServiceTypeVersion)[0].textContent// 获取layerconst layerDom xmlDom.ge…...

LocalForage 使用指南:统一管理 LocalStorage、WebSQL 和 IndexedDB

前言 在前端开发中&#xff0c;客户端数据存储是一个至关重要的环节。无论是用户偏好设置、缓存内容&#xff0c;还是表单数据&#xff0c;都需要一个高效、可靠的存储方案。浏览器原生提供的 LocalStorage、SessionStorage 和 IndexedDB 等 API 虽然功能强大&#xff0c;但使…...

代码随想录算法训练营第五天-哈希-242.有效的字母异位词

这道题的总体感觉不是很难&#xff0c;但是其完成的思想还是很有趣的利用数据下标来代表字母序列然后遍历两个字符串每个字符&#xff0c;给对应字母下标的数组中一个自增&#xff0c;另一个自减通过查看最后的数组内容是不是0&#xff0c;来判断是不是异位词 #include <io…...

学习maven(maven 项目模块化,继承,聚合)

前言 本篇博客的核心&#xff1a;理解maven 项目模块化&#xff0c;继承&#xff0c;聚合 的含义 maven 项目模块化 含义 maven项目模块化&#xff1a;使用maven 构建项目&#xff0c;管理项目的方式&#xff0c;我们可以将maven项目根据内在的关系拆分成很多个小项目【模块】…...

KDD 2025预讲会:10位一作的论文分享与话题思辨|12月18日全天直播

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 圆桌思辨&#xff1a;一作们的KDD 2025投稿经验分享与热点探讨 1. KDD 2025 与往年相比有哪些新变化&#xff1f;两次投稿周期的新规则有哪些影响&#xff1f; 2. 第一篇KDD的工作是如何成功被接收的&#xff1…...

掌握特征提取:机器学习中的 PCA、t-SNE 和 LDA模型

文章目录 一、说明二、既然有 PCA 技术降维&#xff0c;为什么还要学习 t-SNE&#xff1f;2.1 t-SNE的核心思想&#xff1a;2.2 保持点之间的局部关系有什么意义&#xff1f;2.3 t-SNE 的几何直觉&#xff1a; 三、t-SNE 的数学公式&#xff1a;四、目标函数&#xff1a;五、梯…...

JAVA基础:注释

JAVA基础:注释 作用 使得代码中的一段文本不被执行,起到解释说明的作用。 分类 JAVA中的注释有三种: 单行注释 //单行注释多行注释 /* 多 行 注 释 */文档注释 /***@deprecated comments* @author lhy*/文档注释可以添加一些参数作为说明。 有趣的代码注释 卡车/* * *…...

从源码构建安装Landoop kafka-connect-ui

背景 部署Landoop kafka-connect-ui最简单的办法还是通过docker来部署&#xff0c;我们之前的kafka-connect-ui就是通过docker部署的&#xff0c;但是&#xff0c;最近发现个问题&#xff1a;当使用docker部署且防火墙使用的是firewalld的情况下&#xff0c;就会出现端口冲突。…...

【自动驾驶】Ubuntu22.04源码安装Autoware Core/Universe

【自动驾驶】Ubuntu22.04源码安装Autoware Core/Universe 官方源码安装教程前置条件安装ROS2 Humble安装Autoware Core/Universe配置开发环境配置工作空间设置控制台 官方源码安装教程 链接&#xff1a;https://autowarefoundation.github.io/autoware-documentation/main/ins…...

使用Nexus3搭建npm私有仓库

一、npm介绍 npm的全称是Node Package Manager&#xff0c;它是一个开放源代码的命令行工具&#xff0c;用于安装、更新和管理Node.js模块。npm是Node.js的官方模块管理器&#xff0c;它允许用户从一个集中的仓库中下载和安装公共的Node.js模块&#xff0c;并将这些模块集成到…...

OpenHarmony和OpenVela的技术创新以及两者对比

两款有名的国内开源操作系统&#xff0c;OpenHarmony&#xff0c;OpenVela都非常的优秀。本文对二者的创新进行一个简要的介绍和对比。 一、OpenHarmony OpenHarmony具有诸多有特点的技术突破和重要贡献&#xff0c;以下是一些主要方面&#xff1a; 架构设计创新 分层架构…...

【LeetCode每日一题】Leetcode 1071.字符串的最大公因子

Leetcode 1071.字符串的最大公因子 题目描述&#xff1a; 对于字符串 s 和 t&#xff0c;只有在 s t t t … t t&#xff08;t 自身连接 1 次或多次&#xff09;时&#xff0c;我们才认定 t 能除尽 s。 给定两个字符串 str1 和 str2 。返回 最长字符串 x&#xff0c;要…...

《C++:计算机视觉图像识别与目标检测算法优化的利器》

在当今科技飞速发展的时代&#xff0c;计算机视觉领域正经历着前所未有的变革与突破。图像识别和目标检测作为其中的核心技术&#xff0c;广泛应用于安防监控、自动驾驶、智能医疗等众多领域&#xff0c;其重要性不言而喻。而 C语言&#xff0c;凭借其卓越的性能、高效的资源控…...

大模型的构建与部署(2)——数据清洗

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl1. 数据清洗的必要性与影响 1.1 数据清洗对模型性能的影响 数据清洗是数据预处理的关键步骤,对于模型训练的性能和准确性有着直接的影响。原始数据中的缺失值、重复值、异常值以及数据格式不一致…...

试题转excel;word转excel;大风车excel

一、问题描述 一名教师朋友&#xff0c;偶尔会需要整理一些高质量的题目到excel中 以往都是手动复制搬运&#xff0c;几百道题几乎需要一个下午的时间 关键这些事&#xff0c;枯燥无聊费眼睛&#xff0c;实在是看起来就很蠢的工作 就想着做一个工具&#xff0c;可以自动处理…...

微信小程序webview和小程序通讯

1.背景介绍 1.1需要在小程序嵌入vr页面&#xff0c;同时在vr页面添加操作按钮与小程序进行通信交互 1.2开发工具&#xff1a;uniapp开发小程序 1.3原型图 功能&#xff1a;.点击体验官带看跳转小程序的体验官带看页面 功能&#xff1a;点击立即咨询唤起小程序弹窗打电话 2.…...

ChatGPT大模型 创作高质量文案的使用教程和案例

引言 随着人工智能技术的飞速发展,大语言模型如 ChatGPT 在创作文案、生成内容方面展现出了强大的能力。无论是个人用户还是企业用户,都可以利用 ChatGPT 提高工作效率、激发创意、甚至解决实际问题。本文将详细介绍 ChatGPT 如何帮助创作各类高质量文案,并通过具体案例展示…...

Vue Web开发(八)

1. VueWeb面包屑和tag的布局 本章节完成VueWeb面包屑和tag的布局&#xff0c;并且与左侧菜单联系&#xff0c;涉及组件间通信。 1.1. 页面创建 &#xff08;1&#xff09;首先我们先完成每个页面的路由&#xff0c;之前已经有home页面和user页面&#xff0c;缺少mail页面和其…...

element-ui实现table表格的嵌套(table表格嵌套)功能实现

最近在做电商类型的官网&#xff0c;希望实现的布局如下&#xff1a;有表头和表身&#xff0c;所以我首先想到的就是table表格组件。 表格组件中常见的就是&#xff1a;标题和内容一一对应&#xff1a; 像效果图中的效果&#xff0c;只用基础的表格布局是不行的&#xff0c;因…...

【考前预习】4.计算机网络—网络层

往期推荐 【考前预习】3.计算机网络—数据链路层-CSDN博客 【考前预习】2.计算机网络—物理层-CSDN博客 【考前预习】1.计算机网络概述-CSDN博客 目录 1.网络层概述 2.网络层提供的两种服务 3.分类编址的IPV4 4.无分类编址的IPV4—CIDR 5.IPV4地址应用规划 5.1使用定长子…...

【java】MDC

目录 1. 说明2. 作用3. 使用4. 与TraceID的关系5. 注意事项 1. 说明 1.MDC&#xff08;Mapped Diagnostic Context&#xff09;是一个用于在多线程环境中追踪和传递日志上下文信息的机制。2.映射诊断环境。3.MDC是一个线程本地的、可维护的、可传递的上下文环境。4.它允许开发…...

Android 好的开源库

1. 权限请求框架 GitHub - getActivity/XXPermissions: Android 权限请求框架&#xff0c;已适配 Android 14 2. 下载框架 GitHub - lingochamp/okdownload: A Reliable, Flexible, Fast and Powerful download engine....

Go 语言结构

Go 语言结构 Go 语言,也称为 Golang,是一种由 Google 开发和支持的静态类型、编译型编程语言。它于 2009 年首次发布,旨在提高多核处理器、网络资源和大型代码库的性能。Go 语言以其简洁的语法、并发支持和强大的标准库而闻名,特别适合构建高性能的网络服务和分布式系统。…...

【漆学军】MT5几个重要类库的使用例子

MT5编程&#xff0c;有两种方式&#xff0c;一种是函数式编程&#xff0c;一种是面向对象编程。 面向对象编程&#xff0c;会让我们编写代码变得非常简单。 面向对象编程&#xff0c;主要是要熟悉4个类库。 #include <Trade\PositionInfo.mqh> #include <Trade\Tra…...

在 Ubuntu 24.04.1 LTS (WSL) 中使用 openssl 生成 keybox.xml

看到“生成 keybox.xml”&#xff0c;大概率都会联想到 PIF 和 Tricky Store。这里就不多解释它们的用途了。最近在网上看到生成非 AOSP keybox 的教程&#xff0c;在这里做一些补充&#xff0c;并将代码打包成一个 Python 脚本。 参考自&#xff1a; Idea 提供者&#xff1a…...

【JavaSE基础】第十六章:IO流

一、理解 1.简单而言&#xff1a;流就是内存与存储设备之间传输数据的通道、管道。 2.流的分类&#xff1a; (1) 按方向 ( 以 JVM 虚拟机为参照物 ) 【重点】 输入流&#xff1a;将< 存储设备 > 中的内容读入到 < 内存 > 中。 输…...

常见漏洞—SSRF_FastCGI

FastCGI协议 简介 Fast CGI源自旧版本的CGI 路由/结构图 # 访问url --> 浏览器生成HTTP请求报文 --> web server解析请求&#xff08;例如nginx&#xff09; web server 是内容的分发者 当访问静态页面时&#xff0c;web server 会直接返回资源&#xff0c;例如index.htm…...