草庐IT

图像边缘检测

crossoverpptx 2023-12-16 原文

1. 什么是边缘检测

边缘检测是图像处理与计算机视觉中的重要技术之一。其目的是检测识别出图像中亮度变化剧烈的像素点构成的集合。图像边缘的正确检测对于分析图像中的内容、实现图像中物体的分割、定位等具有重要的作用。边缘检测大大减少了源图像的数据量,剔除了与目标不相干的信息,保留了图像重要的结构属性。

图像的边缘指的是图像中像素灰度值突然发生变化的区域,如果将图像的每一行像素和每一列像素都描述成一个关于灰度值的函数,那么图像的边缘对应在灰度值函数中是函数值突然变大的区域。函数值的变化趋势可以用函数的导数描述。当函数值突然变大时,导数也必然会变大,而函数值变化较为平缓区域,导数值也比较小,因此可以通过寻找导数值较大的区域去寻找函数中突然变化的区域,进而确定图像中的边缘位置。

2 边缘检测的常用方法及Python应用

边缘检测的方法大致可分为两类:基于搜索和基于零交叉。

基于搜索的边缘检测方法:首先计算边缘强度,通常用一阶导数表示,例如梯度模,然后,计算估计边缘的局部方向,通常采用梯度的方向,并利用此方向找到局部梯度模的最大值。

基于零交叉的边缘检测方法:找到由图像得到的二阶导数的零交叉点来定位边缘,通常用拉普拉斯算子或非线性微分方程的零交叉点。

滤波作为边缘检测的预处理通常是必要的,通常采用高斯滤波。

2.1 一阶微分算子

一阶微分为基础的边缘检测,通过计算图像的梯度值来检测图像的边缘,如Roberts算子、Prewitt算子和Sobel算子等。

2.1.1 Roberts算子

Roberts算子是一种最简单的算子,它利用局部差分算子寻找边缘。采用对角线相邻两像素之差近似梯度幅值检测边缘,检测垂直边缘的效果比斜向边缘要好,定位精度高,但对噪声比较敏感,无法抑制噪声的影响。

Roberts算子是一个2x2的模板,采用的是对角方向相邻的两个像素之差,如下的2个卷积核形成了Roberts算子,图像中的每一个点都用这2个核做卷积:

若对于输入图像f(x,y),使用Roberts算子后输出的目标图像为g(x,y),则

在Python中,Roberts算子主要是通过Numpy定义模板,再调用OpenCV的filter2D()函数实现边缘提取。该函数主要是利用内核实现对图像的卷积运算,其函数原型如下:

dst = filter2D(src, ddepth, kernel, dts, anchor,delta, borderType)

参数说明:
src:表示输入图像;
ddepth: 表示目标图像所需的深度;
kernel: 表示卷积核,一个单通道浮点型矩阵;
anchor: 表示内核的基准点,其默认值为(-1, -1),位于中心位置;
delta:表示在存储目标图像前可选的添加到像素的值,默认值为0;
borderType:表示边框模式。

实验代码如下:

 def Roberts(srcImg_path):
    img = cv2.imread(srcImg_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # Roberts算子
    kernelx = np.array([[1, 0], [0, -1]], dtype=int)
    kernely = np.array([[0, -1], [1, 0]], dtype=int)
    x = cv2.filter2D(grayImage, cv2.CV_16S, kernelx)
    y = cv2.filter2D(grayImage, cv2.CV_16S, kernely)
    # 转成uint8
    absX = cv2.convertScaleAbs(x)
    absY = cv2.convertScaleAbs(y)
    Roberts = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
    # 显示图形
    titles = ["Original Image", "Roberts Image"]
    images = [img, Roberts]
    for i in range(2):
        plt.subplot(1, 2, i+1)
        plt.imshow(images[i], "gray")
        plt.title(titles[i])
        plt.axis('off')
    plt.show()

效果如下:

2.1.2 Prewitt算子

Prewitt是一种图像边缘检测的微分算子,其原理是利用特定区域内像素值产生的差分实现边缘检测。由于Prewitt算子采用3x3模板对区域内的像素值进行计算,而Roberts算子的模板为2x2,故Prewitt算子的边缘检测结果在水平和垂直方向均比Roberts算子更加明显。Prewitt算子适合用来识别噪声较多,灰度渐变的图像。

Prewitt算子卷积核如下:

实验代码如下:

 def Prewitt(srcImg_path):
    img = cv2.imread(srcImg_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # Prewitt算子
    kernelx = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]], dtype=int)
    kernely = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=int)
    x = cv2.filter2D(grayImage, cv2.CV_16S, kernelx)
    y = cv2.filter2D(grayImage, cv2.CV_16S, kernely)
    # 转成uint8
    absX = cv2.convertScaleAbs(x)
    absY = cv2.convertScaleAbs(y)
    Prewitt = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
    # 显示图形
    titles = ["Original Image", "Prewitt Image"]
    images = [img, Prewitt]
    for i in range(2):
        plt.subplot(1, 2, i+1)
        plt.imshow(images[i], "gray")
        plt.title(titles[i])
        plt.axis('off')
    plt.show()

效果如下:

2.1.3 Sobel算子

在边缘检测中,常用的一种模板是Sobel算子。Sobel算子有两个卷积核,一个是检测水平边缘的;另一个是检测垂直边缘的。与Prewitt算子相比,Sobel算子对于像素的位置的影响做了加权,可以降低边缘模糊程度,因此效果更好。

Sobel算子卷积核如下:

在opencv-python中定义了Sobel算子,其函数原型如下:

dst = Sobel(src, ddepth, dx, dy, dst,ksize, scale, delta, borderType)

参数说明:
src:表示输入图像;
dst:表示输出的边缘图,其大小和通道数与输入图像相同;
ddepth:表示目标图像所需的深度,针对不同的输入图像,输出目标图像有不同的深度;
dx:表示x方向上的差分阶数,取值1或0;
dy:表示y方向上的差分阶数,取值1或0;
ksize:表示Sobel算子的大小,其值必须是正数和奇数;
scale:表示缩放导数的比例常数,默认情况下没有伸缩系数;
delta:表示将结果存入目标图像之前,添加到结果中的可选增量值。

实验代码如下:

 def Sobel_demo(srcImg_path):
    img = cv2.imread(srcImg_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # Sobel算子
    x = cv2.Sobel(grayImage, cv2.CV_16S, 1, 0)
    y = cv2.Sobel(grayImage, cv2.CV_16S, 0, 1)
    # 转成uint8
    absX = cv2.convertScaleAbs(x)
    absY = cv2.convertScaleAbs(y)
    Sobel = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
    # 显示图形
    titles = ["Original Image", "Sobel Image"]
    images = [img, Sobel]
    for i in range(2):
        plt.subplot(1, 2, i+1)
        plt.imshow(images[i], "gray")
        plt.title(titles[i])
        plt.axis('off')
    plt.show()

效果如下:

2.2 二阶微分算子

二阶微分为基础的边缘检测,通过寻求二阶导数中的过零点来检测边缘,如Laplacian算子和Canny算子等。

2.2.1 Laplacian算子

Laplacian算子是n维欧几里德空间中的一个二阶微分算子,常用于图像增强和边缘提取。它通过灰度差分计算邻域内的像素,基本流程是:判断图像中心像素灰度值与它周围其他像素的灰度值,如果中心像素的灰度更高,则提升中心像素的灰度;反之降低中心像素的灰度,从而实现图像锐化操作。在算法实现过程中,Laplacian算子通过对邻域中心像素的四方向或八方向求梯度,再将梯度相加起来判断中心像素灰度与邻域内其他像素灰度的关系,最后通过梯度运算的结果对像素灰度进行调整。

在opencv-python中,Laplacian算子封装在Laplacian()函数中,其函数原型如下:

dst = Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])

参数说明:
src:表示输入图像;
dst:表示输出的边缘图,其大小和通道数与输入图像相同;
ddepth:表示目标图像所需的深度;
ksize:表示用于计算二阶导数的滤波器的孔径大小,其值必须是正数和奇数,且默认值为1;
scale:表示计算拉普拉斯算子值的可选比例因子,默认值为1;
delta:表示将结果存入目标图像之前,添加到结果中的可选增量值,默认值为0;
borderType:表示边框模式。

当ksize=1时,Laplacian()函数采用3x3模板(四邻域)进行变换处理。下面的实验代码是采用ksize=3的Laplacian算子进行图像锐化处理:

def Laplacian_demo(srcImg_path):
    img = cv2.imread(srcImg_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # Laplacian算子
    Laplacian = cv2.Laplacian(grayImage, cv2.CV_16S, ksize=3)
    # 转成uint8
    Laplacian = cv2.convertScaleAbs(Laplacian)
    # 显示图形
    titles = ["Original Image", "Laplacian Image"]
    images = [img, Laplacian]
    for i in range(2):
        plt.subplot(1, 2, i+1)
        plt.imshow(images[i], "gray")
        plt.title(titles[i])
        plt.axis('off')
    plt.show()

效果如下:

2.2.2 Canny算子

Canny算子由John F. Canny在1986年提出,由于它出色的检测和容错能力,至今一直被广泛使用。Canny边缘检测具有以下特点:
较低的错误率 - 只有真实存在的边缘才会被检测到。
较好的边缘定位 - 检测出来的结果和图像中真实的边缘在距离上的误差很小。
没有重复的检测 - 对于每一条边缘,只会返回一个与之对应的结果。

Canny算子的计算步骤大概分成以下几步:

1.图像灰度化
2.用高斯滤波去噪:目的是平滑一些纹理较弱的非边缘区域,以得到更准确的边缘。
3.计算梯度方向和大小:图像梯度表达的是各个像素点之间,像素值大小的变化幅度大小,变化较大,则可以认为是处于边缘位置。
4.非极大值抑制:在获得梯度的方向和大小之后,应该对整幅图像做一个扫描,去除那些非边界上的点,即对每一个像素进行检查,看这个点的梯度是不是周围具有相同梯度方向的点中最大的。
5.双阈值选取和滞后边界跟踪:确定哪些边界才是真正的边界。这时我们需要设置两个阈值:minVal和maxVal。当图像的灰度梯度高于maxVal时被认为是真的边界,那些低于minVal的边界会被抛弃。如果介于两者之间的话,就要看这个点是否与某个被确定为真正的边界点相连,如果是就认为它也是边界点,如果不是就抛弃。

在Python Opencv接口中,提供了Canny函数,其函数原型如下:

canny = cv2.Canny(image,threshold1,threshold2)

参数说明:
image:灰度图;
threshold1:minval,较小的阈值将间断的边缘连接起来;
threshold2:maxval,较大的阈值检测图像中明显的边缘。

实验代码如下:

def Canny_demo(srcImg_path):
    img = cv2.imread(srcImg_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 高斯滤波
    img_GaussianBlur = cv2.GaussianBlur(gray, (3,3), 0)
    # Canny算子
    Canny = cv2.Canny(img_GaussianBlur, 0, 100)
    # 显示图形
    titles = ["Original Image", "Canny Image"]
    images = [img, Canny]
    for i in range(2):
        plt.subplot(1, 2, i+1)
        plt.imshow(images[i], "gray")
        plt.title(titles[i])
        plt.axis('off')
    plt.show()

效果如下:

有关图像边缘检测的更多相关文章

  1. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  2. ruby-on-rails - 添加回形针新样式不影响旧上传的图像 - 2

    我有带有Logo图像的公司模型has_attached_file:logo我用他们的Logo创建了许多公司。现在,我需要添加新样式has_attached_file:logo,:styles=>{:small=>"30x15>",:medium=>"155x85>"}我是否应该重新上传所有旧数据以重新生成新样式?我不这么认为……或者有什么rake任务可以重新生成样式吗? 最佳答案 参见Thumbnail-Generation.如果rake任务不适合你,你应该能够在控制台中使用一个片段来调用重新处理!关于相关公司

  3. ruby-on-rails - 在 Ruby (on Rails) 中使用 imgur API 获取图像 - 2

    我正在尝试使用Ruby2.0.0和Rails4.0.0提供的API从imgur中提取图像。我已尝试按照Ruby2.0.0文档中列出的各种方式构建http请求,但均无济于事。代码如下:require'net/http'require'net/https'defimgurheaders={"Authorization"=>"Client-ID"+my_client_id}path="/3/gallery/image/#{img_id}.json"uri=URI("https://api.imgur.com"+path)request,data=Net::HTTP::Get.new(path

  4. python ffmpeg 使用 pyav 转换 一组图像 到 视频 - 2

    2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p

  5. ruby - 是否有将图像文件转换为 ASCII 艺术的命令行程序或库? - 2

    有这样的事吗?我想在Ruby程序中使用它。 最佳答案 试试这个http://csl.sublevel3.org/jp2a/此外,Imagemagick可能还有一些东西 关于ruby-是否有将图像文件转换为ASCII艺术的命令行程序或库?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/6510445/

  6. ruby-on-rails - 使用 Dragonfly 从 URL 分配图像 - 2

    我正在使用Dragonfly在Rails3.1应用程序上处理图像。我正在努力通过url将图像分配给模型。我有一个很好的表格:{:multipart=>true}do|f|%>RemovePicture?Dragonfly的文档指出:Dragonfly提供了一个直接从url分配的访问器:@album.cover_image_url='http://some.url/file.jpg'但是当我在控制台中尝试时:=>#ruby-1.9.2-p290>picture.image_url="http://i.imgur.com/QQiMz.jpg"=>"http://i.imgur.com/QQ

  7. Ruby-vips 图像处理库。有什么好的使用示例吗? - 2

    我对图像处理完全陌生。我对JPEG内部是什么以及它是如何工作一无所知。我想知道,是否可以在某处找到执行以下简单操作的ruby​​代码:打开jpeg文件。遍历每个像素并将其颜色设置为fx绿色。将结果写入另一个文件。我对如何使用ruby​​-vips库实现这一点特别感兴趣https://github.com/ender672/ruby-vips我的目标-学习如何使用ruby​​-vips执行基本的图像处理操作(Gamma校正、亮度、色调……)任何指向比“helloworld”更复杂的工作示例的链接——比如ruby​​-vips的github页面上的链接,我们将不胜感激!如果有ruby​​-

  8. ruby-on-rails - 如何播种图像的路径? - 2

    Organization和Image具有一对一的关系。Image有一个名为filename的列,它存储文件的路径。我在Assets管道中包含这样一个文件:app/assets/other/image.jpg。播种时如何包含此文件的路径?我已经在我的种子文件中尝试过:@organization=...@organization.image.create!(filename:File.open('app/assets/other/image.jpg'))#Ialsotried:#@organization.image.create!(filename:'app/assets/other/i

  9. ruby-on-rails - 安全地显示使用回形针 gem 上传的图像 - 2

    默认情况下:回形针gem将所有附件存储在公共(public)目录中。出于安全原因,我不想将附件存储在公共(public)目录中,所以我将它们保存在应用程序根目录的uploads目录中:classPost我没有指定url选项,因为我不希望每个图像附件都有一个url。如果指定了url:那么拥有该url的任何人都可以访问该图像。这是不安全的。在user#show页面中:我想实际显示图像。如果我使用所有回形针默认设置,那么我可以这样做,因为图像将在公共(public)目录中并且图像将具有一个url:Someimage:看来,如果我将图像附件保存在公共(public)目录之外并且不指定url(同

  10. ruby - Paperclip:以编程方式分配图像并设置其名称 - 2

    使用Paperclip,我想从这样的URL抓取图像:require'open-uri'user.photo=open(url)问题是我最后得到一个像“open-uri20110915-4852-1o7k5uw”这样的文件名。有什么方法可以更改user.photo上的文件名?作为一个额外的变化,Paperclip将我的文件存储在S3上,所以如果我可以在初始分配中设置我想要的文件名就更好了,这样图像就会上传到正确的S3key。像这样:user.photo=open(url),:filename=>URI.parse(url).path 最佳答案

随机推荐