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 等)解决了这个问题。因为它们具有内置的高斯模糊内核。这减少了从输入图像中获得的噪声。由于找到它们涉及的计算量更高,它们还导致更准确的边缘检测。