艾巴生活网

您现在的位置是:主页>科技 >内容

科技

什么是边缘检测(边缘检测的算法由来)

2024-12-03 13:04:57科技帅气的蚂蚁
指导阅读分析了Canny算法的优缺点,给出了OpenCV利用深度学习进行边缘检测的过程。文末有代码链接。在本文中,我们将学习如何在OpenCV中使

什么是边缘检测(边缘检测的算法由来)

指导阅读

分析了Canny算法的优缺点,给出了OpenCV利用深度学习进行边缘检测的过程。文末有代码链接。

在本文中,我们将学习如何在OpenCV中使用基于深度学习的边缘检测,它比目前流行的canny边缘检测器更准确。边缘检测在很多情况下都是有用的,例如视觉显著性检测、目标检测、跟踪和运动分析、运动结构、三维重建、自动驾驶、图像到文本分析等等。

什么是边缘检测?边缘检测是计算机视觉中一个非常古老的问题,它涉及到检测图像的边缘来确定目标的边界,从而分离出感兴趣的目标。最流行的边缘检测技术之一是Canny边缘检测,它已经成为大多数计算机视觉研究人员和从业人员的首选。让让我们快速看看精明的边缘检测。

Canny边缘检测算法1983年,John Canny在麻省理工学院发明了Canny边缘检测。它认为边缘检测是一个信号处理问题。核心思想是,如果你观察图像中每个像素的强度变化,在边缘是非常高的。

在下面这张简单的图片中,强度的变化只发生在边界处。所以,你可以很容易地通过观察像素强度的变化来识别边缘。

现在,看这张照片。强度不是恒定的,但强度的变化率在边缘处最高。(微积分复习:可以用一阶导数(梯度)计算变化率。)

Canny边缘检测器分4步识别边缘:

去噪:因为这种方法依赖于强度的突然变化,如果图像有很多随机噪声,那么将噪声作为边缘。所以,它用55高斯滤波器平滑你的图像是一个非常好的主意。

梯度计算:接下来,我们计算图像中每个像素的强度的梯度(强度变化率)。我们也计算梯度的方向。

渐变方向垂直于边缘,它映射到四个方向中的一个(水平、垂直和两个对角线方向)。

非最大抑制:现在,我们要删除不是边缘的像素(将其值设置为0)。你可能会说,我们可以简单地选择梯度值最高的像素,这些就是我们的边缘。然而,在真实图像中,梯度不是简单地仅在一个像素处达到峰值,而是在边缘附近的像素处非常高。因此,我们在梯度方向上取33附近的局部最大值。

滞后阈值:在下一步中,我们需要决定一个梯度的阈值,低于该阈值的所有像素将被抑制(设置为0)。Canny边缘检测器采用迟滞阈值法。迟滞阈值法是一种非常简单有效的方法。我们使用两个阈值,而不是一个:

高阈值=选择一个非常高的值,这样任何梯度值高于该值的像素都肯定是边缘。

低阈值=选择一个非常低的值,任何梯度值低于这个值的像素肯定不是边缘。

将检查具有这两个阈值之间的梯度的像素。如果它们连接到边缘,它们将留下来,否则它们将被移除。

滞后阈值

Canny边缘检测问题:

由于Canny边缘检测器只关注局部变化,没有语义理解(理解图像的内容),准确性有限(往往如此)。

Canny边缘检测器在这种情况下会失败,因为图像的上下文不被理解。

语义理解对于边缘检测至关重要,这就是为什么使用机器学习或深度学习的基于学习的检测器比canny边缘检测器产生更好的结果。

OpenCV OpenCV中基于深度学习的边缘检测在其全新的DNN模块中集成了基于深度学习的边缘检测技术。需要OpenCV 3.4.3或更高版本。这种技术称为全局嵌套边缘检测或HED,是一种基于学习的端到端边缘检测系统。它使用类似于vgg的修剪卷积神经网络来执行图像到图像的预测任务。

HED利用中间层的输出。前一层的输出称为侧输出,融合所有五个卷积层的输出,生成最终的预测。由于每一层生成的特征图大小不同,可以有效查看不同尺度的图像。

网络结构:整体嵌套边缘检测

HED方法不仅比其他基于深度学习的方法更准确,而且比其他方法快得多。那这就是OpenCV决定将其集成到新的DNN模块中的原因。以下是本文的研究结果:

在OpenCV中训练深度学习边缘检测代码

OpenCV使用的预训练模型已经在Caffe框架中进行了训练,可以这样加载:

sh download_pretrained.sh

网络中有一个crop层,默认不实现,需要我们自己实现。

类CropLayer(对象):

def __init__(self,params,blobs):

self.xstart=0

self.xend=0

self.ystart=0

self.yend=0

#我们的层接收两个输入。我们需要裁剪第一个输入斑点

#以匹配第二个的形状(保持批次大小和通道数量)

def getMemoryShapes(自身,输入):

inputShape,targetShape=inputs[0],inputs[1]

batchSize,numChannels=inputShape[0],inputShape[1]

高度,宽度=目标形状[2],目标形状[3]

self . ystart=(input shape[2]-target shape[2])//2

self . xstart=(input shape[3]-target shape[3])//2

self.yend=self.ystart高度

self.xend=self.xstart宽度

return [[batchSize,numChannels,height,width]]

向前定义(自身,输入):

return [inputs[0][:self.ystart:self.yend,self.xstart:self.xend]]

现在,我们可以重载这个类,只用一行代码注册这个层。

cv.dnn_registerLayer('Crop 'CropLayer)

现在,我们准备构建网络图并加载权重,这可以通过OpenCV的dnn.readNe函数来完成

net=cv . dnn . readnet(args . proto txt,args.caffemodel)

现在,下一步是批量加载图像,并通过网络运行它们。为此,我们使用cv2.dnn.blobFromImage方法。该方法从输入图像创建四维斑点。

blob=cv . dnn . blobfromimage(image,scalefactor,size,mean,swapRB,crop)

其中包括:

Image:是我们要发送给神经网络进行推理的输入图像。

Scalefactor:图像缩放常数。很多时候我们需要把uint8的图像除以255,这样所有像素都在0和1之间。默认值为1.0,无缩放。

Size:输出图像的空间大小。它将等于后续神经网络作为blobFromImage输出所需的输入大小。

SwapRB:布尔值,指示我们是否要交换3通道图像中的第一个和最后一个通道。OpenCV默认图像是BGR格式,但是如果我们想将这个顺序转换成RGB,我们可以将这个标志设置为True,这也是默认值。

均值:为了归一化,有时我们会计算训练数据集上的平均像素值,并在训练时从每幅图像中减去它。如果我们在训练中确实意味着减法,那么我们必须在推理中应用它。这个平均值是对应于R、G和B通道的元组。比如Imagenet数据集的平均值R=103.93,G=116.77,B=123.68。如果我们使用swapRB=False,那么这个顺序将是(b,g,r)。

Crop: Boolean标志,指示我们是否要在中心裁剪图像。如果设置为True,当从中心裁剪输入图像时,较小的尺寸等于相应的尺寸,而其他尺寸等于或大于该尺寸。但是,如果我们将其设置为False,它将保持纵横比,只是将其调整为固定大小。

在我们的场景中:

inp=cv.dnn.blobFromImage(frame,scalefactor=1.0,size=(args.width,args.height),

mean=(104.00698793,116.66876762,122.67891434),swapRB=False,

crop=False)

现在,我们只需要调用forward方法。

net.setInput

out=net.forward()

out=out[0,0]

out=cv.resize(out,(frame.shape[1],frame.shape[0])

out=255 * out

out=out.astype(np.uint8)

out=cv.cvtColor(out,cv .COLOR_GRAY2BGR)

连接帧,输出),轴=1)

简历imshow(昆那姆,康)

结果:

中间的图像是人工标注的图像,右边是皮肤单位剂量的结果

中间的图像是人工标注的图像,右边是皮肤单位剂量的结果

文中的代码:

https://github。com/sankit 1/cv-tricks。com/tree/master/OpenCV/Edge _ detection

英文原文:https://cv-tricks。com/opencv-dnn/edge-detection-hed/Lyn