五、基于导数的图像增强
在本章中,我们将继续讨论图像增强,即改善图像外观或有用性的问题。我们将主要关注用于计算图像梯度/导数的空间滤波技术,以及如何将这些技术用于图像中的边缘检测。首先,我们将从使用一阶(偏)导数的图像梯度的基本概念开始,如何计算离散导数,然后讨论二阶导数/拉普拉斯算子。我们将看到如何使用它们在图像中查找边缘。接下来,我们将讨论使用 Python 图像处理库 PIL、scikit-image
的过滤模块和 SciPy 的ndimage
模块对图像进行锐化/去锐化的几种方法。接下来,我们将了解如何使用不同的过滤器(sobel
、canny
、LoG
等)并将其与图像卷积以检测图像中的边缘。最后,我们将讨论如何计算高斯/拉普拉斯图像金字塔(使用scikit-image
),并使用图像金字塔平滑混合两幅图像。本章涉及的主题如下:
- 图像导数梯度,拉普拉斯
- 锐化和去锐化遮罩(带 PIL,
scikit-image
、SciPyndimage
) - 使用导数和滤波器进行边缘检测(Sobel、Canny、LOG、DOG 等,使用 PIL,
scikit-image
- 图像金字塔(高斯和拉普拉斯)-混合图像(带
scikit-image
图像导数-梯度和拉普拉斯
我们可以用有限差分法计算数字图像的(偏)导数。在本节中,让我们讨论如何计算图像导数、梯度和拉普拉斯函数,以及它们为什么有用。通常,让我们从导入所需的库开始,如以下代码块所示:
import numpy as npfrom scipy import signal, misc, ndimagefrom skimage import filters, feature, img_as_floatfrom skimage.io import imreadfrom skimage.color import rgb2grayfrom PIL import Image, ImageFilterimport matplotlib.pylab as pylab
导数和梯度
下图显示了如何使用有限差分(具有正向和中心差分,后者更精确)计算图像I(这是一个函数f(x,y))的偏导数,可以使用卷积和所示的内核来实现。该图还定义了梯度向量、其大小(对应于边的强度)和方向(垂直于边)。输入图像中强度(灰度值)急剧变化的位置对应于图像一阶导数强度中存在峰值/尖峰(或低谷)的位置。换句话说,梯度幅值中的峰值标记边缘位置,我们需要对梯度幅值设置阈值,以在图像中找到边缘:
下面的代码块显示了如何使用前面显示的卷积核计算梯度(以及幅度和方向),并将灰度国际象棋图像作为输入。它还绘制图像像素值和梯度向量的 x 分量如何随图像中第一行的 y 坐标变化(x=0
):
def plot_image(image, title):
pylab.imshow(image), pylab.title(title, size=20), pylab.axis('off')
ker_x = [[-1, 1]]
ker_y = [[-1], [1]]
im = rgb2gray(imread('../images/chess.png'))
im_x = signal.convolve2d(im, ker_x, mode='same')
im_y = signal.convolve2d(im, ker_y, mode='same')
im_mag = np.sqrt(im_x**2 + im_y**2)
im_dir = np.arctan(im_y/im_x)
pylab.gray()
pylab.figure(figsize=(30,20))
pylab.subplot(231), plot_image(im, 'original'), pylab.subplot(232), plot_image(im_x, 'grad_x')
pylab.subplot(233), plot_image(im_y, 'grad_y'), pylab.subplot(234), plot_image(im_mag, '||grad||')
pylab.subplot(235), plot_image(im_dir, r'$\theta$'), pylab.subplot(236)
pylab.plot(range(im.shape[1]), im[0,:], 'b-', label=r'$f(x,y)|_{x=0}$', linewidth=5)
pylab.plot(range(im.shape[1]), im_x[0,:], 'r-', label=r'$grad_x (f(x,y))|_{x=0}$')
pylab.title(r'$grad_x (f(x,y))|_{x=0}$', size=30)
pylab.legend(prop={'size': 20})
pylab.show()
下图显示了前面代码块的输出。从下图可以看出,x 和 y 方向上的偏导数分别检测图像中的垂直和水平边缘。梯度大小显示图像中不同位置的边缘强度。此外,如果我们从对应于一行(例如,第 0 行)的原始图像中拾取所有像素,我们可以看到一个方波(对应于交替的白色和黑色强度模式),而同一组像素的梯度大小在强度上有尖峰(突然增加/减少),这些对应于(垂直)边缘:
在同一图像上显示幅值和渐变
在前面的示例中,边缘的大小和方向显示在不同的图像中。我们可以创建一个 RGB 图像,并将R、G和B的值设置为如下,以在同一图像中显示大小和方向:
使用与上一个示例中相同的代码,我们仅使用以下代码替换右下子批次代码:
im = np.zeros((im.shape[0],im.shape[1],3))im[...,0] = im_mag*np.sin(im_ang)im[...,1] = im_mag*np.cos(im_ang)pylab.title(r'||grad||+$\theta$', size=30), pylab.imshow(im), pylab.axis('off')
然后,使用老虎图像,我们得到显示的输出。。。
拉普拉斯的
Rosenfeld 和 Kak 已经证明,最简单的各向同性导数算子是拉普拉斯算子,其定义如下图所示。拉普拉斯算子逼近图像的二阶导数并检测边缘。它是各向同性(旋转不变)算子,零交叉标记边缘位置;我们将在本章后面讨论更多关于这一点的内容。换句话说,在输入图像的一阶导数有尖峰/尖峰(或低谷)的位置,在输入图像的二阶导数的相应位置有过零:
关于拉普拉斯算子的几点注记
让我们来看看下面的注释:
- 是一个标量(与梯度不同,梯度是一个向量)
- 使用单个核(掩码)计算拉普拉斯函数(与梯度不同,梯度通常有两个核,x和y方向的偏导数)
- 作为标量,它没有任何方向,因此我们会丢失方向信息
- 是二阶偏导数的和(梯度表示由一阶偏导数组成的向量),但较高的。。。
噪声对梯度计算的影响
用有限差分法计算的导数滤波器对噪声非常敏感。正如我们在上一章中看到的,图像中强度值与其相邻像素非常不同的像素通常是噪声像素。一般来说,噪声越大,强度变化越大,使用滤波器获得的响应越强。下一个代码块向图像添加一些高斯噪声,以查看对梯度的影响。让我们再次考虑图像的一行(精确地行 0),并让我们将强度绘制为{ To.T0} x OrthT1 位置:
from skimage.util import random_noise
sigma = 1 # sd of noise to be added
im = im + random_noise(im, var=sigma**2)
下图显示了在向象棋图像添加一些随机噪声后上一个代码块的输出。如我们所见,向输入图像添加随机噪声对(偏)导数和梯度幅度有很大影响;与边缘相对应的峰值与噪声几乎无法区分,并且图案被破坏:
在应用导数滤波器之前对图像进行平滑应该是有帮助的,因为它会删除可能是噪声的高频分量,并强制(噪声)像素(与它们的邻居不同)看起来更像它们的邻居。因此,解决方案是首先使用 LPF(如高斯滤波器)平滑输入图像,然后在平滑图像中找到峰值(使用阈值)。这就产生了对数滤波器(如果我们使用二阶导数滤波器),我们将在本章后面探讨。
锐化和去锐化掩蔽
锐化的目的是突出显示图像中的细节或增强已模糊的细节。在本节中,我们将讨论一些技术,并通过一些示例演示几种不同的图像锐化方法。
拉普拉斯锐化
可以使用拉普拉斯滤波器通过以下两个步骤锐化图像:
- 将拉普拉斯滤波器应用于原始输入图像。
- 将步骤 1获得的输出图像与原始输入图像相加(以获得锐化图像)。下面的代码块演示了如何使用
scikit-image``filters
模块的laplace()
功能实现上述算法:
from skimage.filters import laplace
im = rgb2gray(imread('../images/me8.jpg'))
im1 = np.clip(laplace(im) + im, 0, 1)
pylab.figure(figsize=(20,30))
pylab.subplot(211), plot_image(im, 'original image')
pylab.subplot(212), plot_image(im1, 'sharpened image')
pylab.tight_layout()
pylab.show()
以下是先前代码块、原始图像和使用先前算法的锐化图像的输出:
反锐化掩蔽
反锐化遮罩是一种锐化图像的技术,从图像本身中减去模糊版本的图像。反锐化掩模使用的典型混合公式如下:锐化=原始+(原始)− 模糊)×金额。
这里,数量是一个参数。接下来的几节将演示如何使用 Python 中的 SciPy 函数的ndimage
模块实现这一点。
使用 SciPy ndimage 模块
如前所述,我们可以首先模糊图像,然后计算细节图像作为原始图像和模糊图像之间的差值,以实现反锐化掩蔽。锐化后的图像可以作为原始图像和细节图像的线性组合来计算。下图再次说明了该概念:
下面的代码块显示了如何使用 SciPyndimage
模块实现灰度图像的反锐化掩模操作(彩色图像也可以这样做,留给读者练习),使用上述概念:
def rgb2gray(im):
'''
the input image is an RGB image
with pixel values for each channel in [0,1]
'''
return np.clip(0.2989 * im[...,0] + 0.5870 * im[...,1] + 0.1140 * im[...,2], 0, 1)
im = rgb2gray(img_as_float(misc.imread('../images/me4.jpg')))
im_blurred = ndimage.gaussian_filter(im, 5)
im_detail = np.clip(im - im_blurred, 0, 1)
pylab.gray()
fig, axes = pylab.subplots(nrows=2, ncols=3, sharex=True, sharey=True, figsize=(15, 15))
axes = axes.ravel()
axes[0].set_title('Original image', size=15), axes[0].imshow(im)
axes[1].set_title('Blurred image, sigma=5', size=15), axes[1].imshow(im_blurred)
axes[2].set_title('Detail image', size=15), axes[2].imshow(im_detail)
alpha = [1, 5, 10]
for i in range(3):
im_sharp = np.clip(im + alpha[i]*im_detail, 0, 1)
axes[3+i].imshow(im_sharp), axes[3+i].set_title('Sharpened image, alpha=' + str(alpha[i]), size=15)
for ax in axes:
ax.axis('off')
fig.tight_layout()
pylab.show()
下面的屏幕截图显示了前面代码块的输出。可以看出,随着α值的增加,输出变得更尖锐:
使用导数和滤波器进行边缘检测(Sobel、Canny 等)
如前所述,构成图像中边缘的像素是图像强度函数中突然快速变化(不连续)的像素,边缘检测的目标是识别这些变化。因此,边缘检测是一种预处理技术,其中输入为 2D(灰度)图像,输出为一组曲线(称为边缘。在边缘检测过程中提取图像的显著特征;使用边缘的图像表示比使用像素的图像表示更紧凑。边缘检测器输出梯度的大小(作为灰度图像),现在,为了获得边缘像素(作为二值图像)。。。
使用偏导数计算梯度幅值
如前所述,使用偏导数的(正向)有限差分近似计算的梯度幅度(可以认为是边缘强度)可用于边缘检测。下面的屏幕截图显示了通过使用与上次相同的代码来计算梯度大小,然后使用斑马输入的灰度图像以[0,1]间隔剪裁像素值而获得的输出:
下面的屏幕截图显示了渐变幅度图像。如图所示,边缘看起来更厚且多像素宽:
为了获得每一个边缘都有一个像素宽的二值图像,我们需要应用非最大值抑制算法,如果像素不是像素邻域中沿梯度方向的局部最大值,则移除该像素。算法的实现留给读者作为练习。以下屏幕截图显示了带有非最大抑制的输出:
非最大值抑制算法
- 该算法首先检查边缘的角度(方向)(由边缘检测器输出)。
- 如果像素值在与其边缘角度相切的直线上不是最大值,则该像素值是要从边缘贴图中删除的候选像素。
- 这是通过将边缘方向(360)拆分为八个相等的间隔(角度为 22.50 度)来实现的。下表显示了不同的情况和要采取的措施:
- 我们可以通过观察π/8 范围并相应地设置一系列 if 条件的切向比较来实现这一点。
- 可以清楚地观察到边缘细化的效果(从上一张图像中)。。。
基于 scikit 图像的 Sobel 边缘检测器
与使用有限差分法相比,(一阶)导数可以更好地近似。下图中所示的 Sobel 运算符经常使用:
Sobel 算子的标准定义中不包括 1/8 项,因为出于边缘检测的目的,它不会产生差异,尽管需要标准化项才能正确获得梯度值。下一个 Python 代码片段展示了如何分别使用scikit-image
的filters
模块的sobel_h()
、sobel_y()
和sobel()
函数来查找水平/垂直边缘,并使用 Sobel 运算符计算梯度大小:
im = rgb2gray(imread('../images/tajmahal.jpg')) # RGB image to gray scale
pylab.gray()
pylab.figure(figsize=(20,18))
pylab.subplot(2,2,1)
plot_image(im, 'original')
pylab.subplot(2,2,2)
edges_x = filters.sobel_h(im)
plot_image(edges_x, 'sobel_x')
pylab.subplot(2,2,3)
edges_y = filters.sobel_v(im)
plot_image(edges_y, 'sobel_y')
pylab.subplot(2,2,4)
edges = filters.sobel(im)
plot_image(edges, 'sobel')
pylab.subplots_adjust(wspace=0.1, hspace=0.1)
pylab.show()
下面的屏幕截图显示了前面代码块的输出。可以看出,图像的水平和垂直边缘由水平和垂直 Sobel 滤波器检测,而使用 Sobel 滤波器计算的梯度幅度图像在两个方向上检测边缘:
具有 scikit 图像的不同边缘检测器–Prewitt、Roberts、Sobel、Scharr 和 Laplace
图像处理算法中使用了很多不同的边缘检测算子;它们都是离散的(一阶或二阶)微分算子,它们试图逼近图像强度函数的梯度(例如,我们前面讨论过的 Sobel 算子)。下图所示的内核是用于边缘检测的几种常用内核。例如,常用的近似于一阶图像导数的导数滤波器是 Sobel、Prewitt、Sharr 和 Roberts 滤波器,而近似于二阶导数的导数滤波器是拉普拉斯滤波器:
如scikit-image
文件中所述。。。
基于 scikit 图像的 Canny 边缘检测器
Canny 边缘检测器是一种流行的边缘检测算法,由 John F.Canny 开发。此算法具有以下多个步骤:
- 平滑/降噪:边缘检测操作对噪声敏感。因此,在一开始,使用 5 x 5 高斯滤波器从图像中去除噪声。
- 计算梯度的大小和方向:然后对图像应用 Sobel 水平和垂直滤波器,以计算每个像素的边缘梯度大小和方向,如前所述。然后将计算出的梯度角(方向)四舍五入为表示每个像素的水平、垂直和两个对角线方向的四个角之一。
-
非最大抑制:在这一步中,边缘被减薄–可能不构成边缘的任何不需要的像素被移除。要做到这一点,每个像素都要检查它是否是其邻域中梯度方向上的局部最大值。结果,获得具有薄边缘的二值图像。
-
链接和滞后阈值:此步骤决定检测到的所有边缘是否都是强边缘。为此,使用了一对(滞后)阈值
min_val
和max_val
。确定边缘是强度梯度值高于max_val
的边缘。确保非边是强度梯度值低于min_val
的边,它们将被丢弃。位于这两个阈值之间的边根据其连通性分类为边或非边。如果它们连接到“确定边缘”像素,则它们被视为边缘的一部分。否则,它们也会被丢弃。这一步还去除了小像素噪声(假设边缘是长线)。
最后,算法输出图像的强边缘。下面的代码块显示了如何使用scikit-image
实现 Canny 边缘检测器:
im = rgb2gray(imread('../images/tiger3.jpg'))
im = ndimage.gaussian_filter(im, 4)
im += 0.05 * np.random.random(im.shape)
edges1 = feature.canny(im)
edges2 = feature.canny(im, sigma=3)
fig, (axes1, axes2, axes3) = pylab.subplots(nrows=1, ncols=3, figsize=(30, 12), sharex=True, sharey=True)
axes1.imshow(im, cmap=pylab.cm.gray), axes1.axis('off'), axes1.set_title('noisy image', fontsize=50)
axes2.imshow(edges1, cmap=pylab.cm.gray), axes2.axis('off')
axes2.set_title('Canny filter, $\sigma=1$', fontsize=50)
axes3.imshow(edges2, cmap=pylab.cm.gray), axes3.axis('off')
axes3.set_title('Canny filter, $\sigma=3$', fontsize=50)
fig.tight_layout()
pylab.show()
下面的屏幕截图显示了前面代码的输出;对于初始高斯 LPF,使用具有不同 sigma 值的 Canny 滤波器检测边缘。如图所示,sigma 值越低,原始图像越不模糊,因此可以找到更多的边缘(更精细的细节):
原木和狗过滤器
高斯(LoG)的拉普拉斯滤波器只是另一个线性滤波器,它是高斯滤波器和拉普拉斯滤波器在图像上的组合。由于 2nd导数对噪声非常敏感,因此在应用拉普拉斯算子之前通过平滑图像来消除噪声始终是一个好主意,以确保噪声不会加剧。由于卷积的关联性,可以将其视为采用高斯滤波器的 2nd导数(拉普拉斯),然后将得到的(组合)滤波器应用于图像,因此称为 LoG。可以使用两个具有不同标度(方差)的高斯(DoG)差有效地近似,如下图所示:
下面的代码块。。。
带有 SciPy ndimage 模块的日志过滤器
SciPyndimage
模块的gaussian_laplace()
功能也可以用来实现日志,如下代码块所示:
img = rgb2gray(imread('../images/zebras.jpg'))
fig = pylab.figure(figsize=(25,15))
pylab.gray() # show the filtered result in grayscale
for sigma in range(1,10):
pylab.subplot(3,3,sigma)
img_log = ndimage.gaussian_laplace(img, sigma=sigma)
pylab.imshow(np.clip(img_log,0,1)), pylab.axis('off')
pylab.title('LoG with sigma=' + str(sigma), size=20)
pylab.show()
以下图像显示了使用具有不同平滑参数σ(高斯滤波器的标准偏差)值的对数滤波器获得的输入图像和输出图像:
基于对数滤波器的边缘检测
下面介绍使用LOG
滤波器进行边缘检测的步骤:
- 首先,输入图像需要平滑(通过与高斯滤波器卷积)。
-
然后,平滑后的图像需要用拉普拉斯滤波器进行卷积,得到输出图像为∇ 2(I(x,y)G(x,y))*。
-
最后,需要计算在最后一步中获得的图像的过零点,如下图所示:
基于过零计算的 Marr 和 Hildreth 边缘检测算法
Marr 和 Hildreth 提出了计算对数卷积图像中的过零点(将边缘检测为二值图像)。通过将对数平滑的图像定义为二值图像来查看其符号,可以识别边缘像素。计算过零的算法如下:
- 首先,将对数卷积图像转换为二值图像,将像素值替换为
1
表示正值,将0
表示负值 - 为了计算过零像素,我们只需查看此二值图像中非零区域的边界
- 边界可以通过查找任何非零像素来找到,该像素的近邻为零
- 因此,对于每个像素,如果它是非零的,考虑它的八个邻居;如果任何相邻像素为零,则该像素可被识别为边缘
此功能的实现留作练习。以下代码块描述了通过零交叉检测到的同一斑马图像的边缘:
fig = pylab.figure(figsize=(25,15))
pylab.gray() # show the filtered result in grayscale
for sigma in range(2,10, 2):
pylab.subplot(2,2,sigma/2)
result = ndimage.gaussian_laplace(img, sigma=sigma)
pylab.imshow(zero_crossing(result)) # implement the function zero_crossing() using the above algorithm
pylab.axis('off')
pylab.title('LoG with zero-crossing, sigma=' + str(sigma), size=20)
pylab.show()
下面的屏幕截图显示了前面代码块的输出,在不同的σ标度下,边缘仅通过零交叉来识别:
前面的图像显示了以 LoG/DoG 作为边缘检测器的零交叉。需要注意的是,过零点形成闭合轮廓。
用 PIL 查找和增强边缘
PIL 的ImageFilter
模块的filter
功能也可用于查找和增强图像中的边缘。以下代码块显示了以UMBC library
图像作为输入的示例:
from PIL.ImageFilter import (FIND_EDGES, EDGE_ENHANCE, EDGE_ENHANCE_MORE)im = Image.open('../images/umbc_lib.jpg')pylab.figure(figsize=(18,25))pylab.subplot(2,2,1)plot_image(im, 'original (UMBC library)')i = 2for f in (FIND_EDGES, EDGE_ENHANCE, EDGE_ENHANCE_MORE): pylab.subplot(2,2,i) im1 = im.filter(f) plot_image(im1, str(f)) i += 1pylab.show()
以下屏幕截图显示了使用不同边缘查找/增强过滤器的上述代码的输出:
图像金字塔(高斯和拉普拉斯)-混合图像
我们可以从原始图像开始,迭代创建更小的图像,首先通过平滑(使用高斯滤波器避免抗锯齿,然后通过子采样(统称为减少)来构建图像的高斯金字塔在每次迭代中从上一级别的图像开始,直到达到最小分辨率。以这种方式创建的图像金字塔称为高斯金字塔。通过单独编辑频带(例如,图像混合),这些功能适用于搜索超范围(例如,模板匹配)、预计算和图像处理任务。类似地,图像的拉普拉斯金字塔可以通过从高斯金字塔中最小尺寸的图像开始,然后通过扩展(上采样加平滑)该级别的图像并从高斯金字塔的下一级别的图像中减去该图像来构建,重复这个过程,直到达到原始图像大小。在本节中,我们将看到如何编写 python 代码来计算图像金字塔,然后查看用于混合两个图像的图像金字塔的应用程序。
带有 scikit 图像变换金字塔模块的高斯金字塔
可以使用scikit-image.transform.pyramid
模块的pyramid_gaussian()
函数计算输入图像的高斯金字塔。从原始图像开始,该函数调用pyramid_reduce()
函数,以递归方式获得平滑和向下采样的图像。下面的代码块演示了如何使用lena
RGB 输入图像计算和显示这样的高斯金字塔:
from skimage.transform import pyramid_gaussianimage = imread('../images/lena.jpg')nrows, ncols = image.shape[:2]pyramid = tuple(pyramid_gaussian(image, downscale=2))pylab.figure(figsize=(20,5))i, n = 1, len(pyramid)for p in pyramid: pylab.subplot(1,n,i), pylab.imshow(p) pylab.title(str(p.shape[0]) ...
具有 scikit 图像变换金字塔模块的拉普拉斯金字塔
可以使用scikit-image.transform.pyramid
模块的pyramid_laplacian()
函数计算输入图像的拉普拉斯金字塔。该函数从原始图像及其平滑版本的差分图像开始,计算下采样图像和平滑图像,并取这两个图像的差分递归计算对应于每个层的图像。创建拉普拉斯金字塔的动机是实现压缩,因为对于 0 左右的可预测值,压缩率更高。
用于计算拉普拉斯金字塔的代码类似于先前用于计算高斯金字塔的代码;这是留给读者的练习。以下屏幕截图显示了lena
灰度图像的拉普拉斯金字塔:
请注意,如果我们使用scikit-image
的pyramid_gaussian()
和pyramid_laplacian()
函数,拉普拉斯金字塔中的最低分辨率图像和高斯金字塔中的最低分辨率图像将是不同的图像,这是我们不想要的。我们想要建立一个拉普拉斯金字塔,其中最小分辨率的图像与高斯金字塔的图像完全相同,因为这将使我们能够仅从其拉普拉斯金字塔构建图像。在接下来的几节中,我们将讨论使用scikit-image
的expand()
和reduce()
函数构建我们自己的金字塔的算法。
构造高斯金字塔
可通过以下步骤计算高斯金字塔:
- 从原始图像开始。
- 在金字塔的每一层迭代计算图像,首先通过平滑图像(使用高斯滤波器),然后对其进行下采样。
- 在图像大小足够小的级别停止(例如,1 x 1)。
- 实现前面算法的函数留给读者作为练习;我们只需在以下函数中添加几行即可完成实现:
from skimage.transform import pyramid_reduce def get_gaussian_pyramid(image): ''' input: an RGB image output: the Gaussian Pyramid of the image as a list ''' gaussian_pyramid = [] # add code here # iteratively ...
仅从拉普拉斯金字塔重建图像
下图显示了如何仅从其拉普拉斯金字塔重建图像,如果我们按照上一节中描述的算法构造图像:
请查看以下代码块:
def reconstruct_image_from_laplacian_pyramid(pyramid):
i = len(pyramid) - 2
prev = pyramid[i+1]
pylab.figure(figsize=(20,20))
j = 1
while i >= 0:
prev = resize(pyramid_expand(prev, upscale=2), pyramid[i].shape)
im = np.clip(pyramid[i] + prev,0,1)
pylab.subplot(3,3,j), pylab.imshow(im)
pylab.title('Level=' + str(j) + ' ' + str(im.shape[0]) + 'x' + str(im.shape[1]), size=20)
prev = im
i -= 1
j += 1
pylab.subplot(3,3,j), pylab.imshow(image)
pylab.title('Original image' + ' ' + str(image.shape[0]) + 'x' + str(image.shape[1]), size=20)
pylab.show()
return im
image = img_as_float(imread('../images/apple.png')[...,:3]) # only use the color channels and discard the alpha
pyramid = get_laplacian_pyramid(get_gaussian_pyramid(image))
im = reconstruct_image_from_laplacian_pyramid(pyramid)
下面的屏幕截图显示了前面代码的输出,即原始图像是如何通过拉普拉斯金字塔(Laplacian pyramid)最终构建的,只需对每个级别的图像使用expand()
操作,并将其迭代添加到下一级别的图像中:
将图像与金字塔混合
假设我们有两个 RGB 彩色输入图像,a(苹果)和B(橙色),以及第三个二值掩模图像,M;这三幅图像的大小都相同。目标是在遮罩M的引导下,将图像A与B混合(如果遮罩图像 M 中的像素值为 1,则表示该像素取自图像A,否则取自图像B。以下算法可用于使用图像A和B的拉普拉斯金字塔混合两幅图像(通过使用来自A和B的拉普拉斯金字塔相同级别的图像的线性组合计算混合金字塔),使用掩模图像M的高斯金字塔的同一层级的权重),然后从中重建输出图像。。。
总结
在本章中,我们首先讨论了使用几种滤波器(Sobel、Prewitt、Canny 等)以及通过计算图像的梯度和拉普拉斯算子对图像进行边缘检测。然后,我们讨论了 LoG/DoG 算子,以及如何实现它们,以及如何利用零交叉检测边缘。接下来,我们讨论了如何计算图像金字塔,并使用拉普拉斯金字塔平滑地混合两幅图像。最后,我们讨论了如何使用scikit-image
检测斑点。完成本章后,读者应该能够使用不同的过滤器在图像中使用 Python 实现边缘检测器(Sobel、Canny 等)。此外,读者应该能够实现过滤器来锐化图像,并使用 LoG/DoG 查找不同比例的边缘。最后,他们应该能够将图像与拉普拉斯/高斯金字塔混合,并在不同尺度空间的图像中实现斑点检测。在下一章中,我们将讨论图像的特征检测和提取技术。
问题
- 使用
skimage.filters
模块的unsharp_mask()
功能和radius
和amount
参数的不同值来锐化图像。 - 使用 PIL
ImageFilter
模块的UnsharpMask()
功能以及radius
和percent
参数的不同值来锐化图像。 - 使用锐化内核[[0,-1,0]、-1,5,-1]、[0,-1,0]]锐化彩色(RGB)图像。(提示:对每个颜色通道逐一使用 SciPy
signal
模块的convolve2d()
功能。) - 使用 SciPy
ndimage
模块,可直接锐化彩色图像(无需逐个锐化单个颜色通道)。 -
使用
skimage.transform
模块的pyramid_laplacian()
函数计算并显示带有lena
灰度输入图像的高斯金字塔。 -
建筑
进一步阅读
- https://web.stanford.edu/class/cs448f/lectures/5.2/Gradient%20Domain.pdf
- https://web.stanford.edu/class/ee368/Handouts/Lectures/2014_Spring/Combined_Slides/11-Edge-Detection-Combined.pdf
- https://www.cs.cornell.edu/courses/cs6670/2011sp/lectures/lec02_filter.pdf
- http://www.cs.toronto.edu/~mangas/teaching/320/slides/CSC320L05.pdf
- http://www.cse.psu.edu/~rtc12/CSE486/讲师 11.pdf
- http://graphics.cs.cmu.edu/courses/15-463/2005_fall/www/Lectures/Pyramids.pdf
- http://www.eng.tau.ac.il/~ipapps/Slides/讲师 05.pdf
- https://sandipanweb.wordpress.com/2017/05/16/some-more-computational-photography-merging-and-blending-images-using-gaussian-and-laplacian-pyramids-in-python/
- http://www.me.umn.edu/courses/me5286/vision/VisionNotes/2017/ME5286-Lecture7-2017-EdgeDetection2.pdf
- https://www.cs.rutgers.edu/~elgammal/classes/cs334/EdgesandContours.pdf
-
http://www.cs.cornell.edu/courses/cs664/2008sp/handouts/edges.pdf
- http://www.cs.cmu.edu/~16385/s17/Slides/4.0_Image_Gradients_ 和 _Gradients_Filtering.pdf
- http://www.hms.harvard.edu/bss/neuro/bornlab/qmbc/beta/day4/marr-hildreth-edge-prsl1980.pdf
- http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.420.3300 &rep=rep1&type=pdf
- http://persci.mit.edu/pub_pdfs/pyramid83.pdf
- http://persci.mit.edu/pub_pdfs/spline83.pdf
- http://ftp.nada.kth.se/CVAP/reports/cvap198.pdf
- https://www.cs.toronto.edu/~mangas/teaching/320/assignments/a3/tcomm83.pdf
- https://www.cs.toronto.edu/~mangas/teaching/320/assignments/a3/spline83.pdf
- http://6.869.csail.mit.edu/fa16/lecture/lecture6.pdf
- https://docs.opencv.org/3.1.0/dc/dff/tutorial_py_pyramids.html