📜  Python – 使用 Pillow 进行边缘检测

📅  最后修改于: 2022-05-13 01:55:46.746000             🧑  作者: Mango

Python – 使用 Pillow 进行边缘检测

边缘检测是一门图像处理学科,它结合了数学方法来查找数字图像中的边缘。边缘检测在内部通过在数字图像上运行过滤器/内核来工作,该过滤器/内核检测图像区域中的不连续性,例如像素的亮度/强度值的明显变化。

边缘检测有两种形式:

  • 基于搜索的边缘检测(一阶导数)
  • 基于零交叉的边缘检测(二阶导数)

一些常见的边缘检测方法有:

  • 拉普拉斯算子或基于拉普拉斯算子的边缘检测(二阶导数)
  • Canny 边缘检测器(一阶导数)
  • Prewitt运算符(一阶导数)
  • Sobel算子(一阶导数)

我们将实现一个拉普拉斯算子,以便在我们后面的一个示例中结合边缘检测。为此,我们将使用pillow库。要安装库,请在命令行中执行以下命令:

pip install pillow

注意:-一些 Linux 发行版倾向于预先安装Python和 Pillow。

有两种方法可以在我们的图像上实现边缘检测。在第一种方法中,我们将使用枕头库 ( ImageFilter.FIND_EDGES ) 中提供的内置方法进行边缘检测。在第二个中,我们将使用PIL.ImageFilter.Kernel()创建一个拉普拉斯滤波器,然后使用该滤波器进行边缘检测。

拉普拉斯内核:-

拉普拉斯核(包括对角线)


示例图像:-

示例图像

方法一:

Python3
from PIL import Image, ImageFilter
  
  
# Opening the image (R prefixed to string
# in order to deal with '\' in paths)
image = Image.open(r"Sample.png")
  
# Converting the image to grayscale, as edge detection 
# requires input image to be of mode = Grayscale (L)
image = image.convert("L")
  
# Detecting Edges on the Image using the argument ImageFilter.FIND_EDGES
image = image.filter(ImageFilter.FIND_EDGES)
  
# Saving the Image Under the name Edge_Sample.png
image.save(r"Edge_Sample.png")


Python3
from PIL import Image, ImageFilter
  
img = Image.open(r"sample.png")
  
# Converting the image to grayscale, as Sobel Operator requires
# input image to be of mode Grayscale (L)
img = img.convert("L")
  
# Calculating Edges using the passed laplican Kernel
final = img.filter(ImageFilter.Kernel((3, 3), (-1, -1, -1, -1, 8,
                                          -1, -1, -1, -1), 1, 0))
  
final.save("EDGE_sample.png")


输出(Edge_Sample.png):

内置函数的输出

解释:-

首先,我们使用Image.open()创建图像的图像对象。然后我们将图像颜色模式转换为灰度,因为拉普拉斯运算符的输入是灰度模式(通常)。然后我们通过指定ImageFilter.FIND_EDGES参数将图像传递给Image.filter()函数,该参数依次在图像顶部运行边缘检测内核。上述函数的输出导致图像具有高强度变化(边缘)为白色阴影,而图像的其余部分为黑色。

方法二:

Python3

from PIL import Image, ImageFilter
  
img = Image.open(r"sample.png")
  
# Converting the image to grayscale, as Sobel Operator requires
# input image to be of mode Grayscale (L)
img = img.convert("L")
  
# Calculating Edges using the passed laplican Kernel
final = img.filter(ImageFilter.Kernel((3, 3), (-1, -1, -1, -1, 8,
                                          -1, -1, -1, -1), 1, 0))
  
final.save("EDGE_sample.png")

输出(EDGE_sample.png):

使用拉普拉斯算子进行边缘检测的卷积后图像

解释:-

首先,我们使用Image.open()创建图像的图像对象。然后我们将图像颜色模式转换为灰度,因为拉普拉斯运算符的输入是灰度模式(通常)。然后我们通过在函数内指定我们的运算符/Kernel 作为参数将图像传递给Image.filter()函数。内核是通过使用ImageFilter.Kernel((3, 3), (-1, -1, -1, -1, 8, -1, -1, -1, -1), 1, 0))指定的创建一个 3 X 3 内核(3 像素宽和 3 像素长),其值为(-1, -1, -1, -1, 8, -1, -1, -1, -1) (如拉普拉斯内核图像)。 1参数(在内核之后)代表 Scale 值,它在每次内核操作后除以最终值,因此我们将该值设置为 1,因为我们不希望对最终值进行任何除法。 0参数(在 Scale 值之后)是在除以 Scale 值之后添加的偏移量。我们将该值设置为 0,因为我们不希望内核卷积之后的最终强度值有任何增量。上述函数的输出导致图像具有高强度变化(边缘)为白色阴影,而图像的其余部分为黑色。

附录——

这两个程序产生了相同的结果。原因是内置函数ImageFilter.FIND_EDGE在内部使用 3 X 3 大小的拉普拉斯内核/运算符。因此,我们最终得到了相同的结果。使用内核而不是依赖内置函数的好处是我们可以根据需要定义内核,这些内核可能/可能不在库中。比如我们可以为模糊、锐化、边缘检测(使用其他内核)等创建一个内核。另外,我特意选择了拉普拉斯算子,以便我们可以保持结果的一致性。

使用拉普拉斯算子的好处:-
快速而体面的结果。其他常见的边缘检测器,如 Sobel(一阶导数),计算成本更高,因为它们需要在两个方向上找到梯度,然后对结果进行归一化。

使用拉普拉斯算子的缺点:-
与拉普拉斯核卷积会导致输出中出现大量噪声。其他边缘检测方法(例如 Sobel、Perwitt Operator 等)解决了这个问题。因为它们具有内置的高斯模糊内核。这减少了从输入图像中获得的噪声。由于找到它们涉及的计算量更高,它们还导致更准确的边缘检测。