Python图像处理经典实例

978-7-115-58895-1
作者: 桑迪潘·戴伊(Sandipan Dey)
译者: 王燕王存岷
编辑: 吴晋瑜
分类: Python

图书目录:

详情

本书提供了相关工具和算法,能帮助读者实现分析和可视化图像处理。本书给出了60 余个具体实例的解决方法,采用“菜谱式”方式组织内容,以期指导读者快速实践图像的分析和可视化处理,应对图像处理中的常见挑战,并学习如何执行复杂的任务,如对象检测、图像分割和使用大型混合数据集的图像重建,以及各种图像增强和图像恢复技术,如卡通化、梯度混合和稀疏字典学习。?本书适合计算机视觉工程师、图像处理工程师、软件工程师和机器学习工程师等专业人士阅读,也适合具有一定Python编程基础并希望进一步了解图像处理的细节和复杂性的读者参考。

图书摘要

版权信息

书名:Python图像处理经典实例

ISBN:978-7-115-58895-1

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。


版  权

著    [印]桑迪潘·戴伊(Sandipan Dey)

译    王 燕 王存岷

责任编辑 吴晋瑜

人民邮电出版社出版发行  北京市丰台区成寿寺路11号

邮编 100164  电子邮件 315@ptpress.com.cn

网址 http://www.ptpress.com.cn

读者服务热线:(010)81055410

反盗版热线:(010)81055315

读者服务:

微信扫码关注【异步社区】微信公众号,回复“e58895”获取本书配套资源以及异步社区15天VIP会员卡,近千本电子书免费畅读。

版 权 声 明

Copyright ©Packt Publishing 2020. First published in the English language under the title Python Image Processing Cookbook (9781789537147). All rights reserved.

本书由英国Packt Publishing公司授权人民邮电出版社有限公司出版。未经出版者书面许可,对本书的任何部分不得以任何方式或任何手段复制和传播。

版权所有,侵权必究。

内 容 提 要

本书提供了相关工具和算法,能帮助读者实现分析和可视化图像处理。本书给出了60余个具体实例的解决方法,采用“菜谱式”方式组织内容,以期指导读者快速实践图像的分析和可视化处理,应对图像处理中的常见挑战,并学习如何执行复杂的任务,如对象检测、图像分割和使用大型混合数据集的图像重建,以及各种图像增强和图像修复技术,如卡通化、梯度混合和稀疏字典学习。

本书适合计算机视觉工程师、图像处理工程师、软件工程师和机器学习工程师等专业人士阅读,也适合具有一定Python编程基础并希望进一步了解图像处理的细节和复杂性的读者参考。

前  言

随着人类在无线设备和移动技术领域的进步,人们对数字图像处理技术——一种用来从不断增长的图像集中提取可用信息的技术——提出了越来越高的要求。通过本书,读者可以全面学习相关工具和算法,了解图像处理分析及可视化方法。

通过书中60余个实例的解决方法,读者能够学会应对图像处理过程中遇到的常见挑战,并掌握如何执行复杂任务,比如应用大型混合数据集进行图像检测、图像分割以及图像重建。此外,有针对性的专门章节还能帮助读者熟悉如何运用各种图像增强技术和图像恢复技术,例如卡通化、梯度混合、稀疏字典学习等。随着学习的深入,读者将能掌握人脸变形和图像分割技术,并对应用基于深度学习的各种技术来解决现实问题有进一步了解。

学完本书,读者能够熟练而有效地应用图像处理技术,能够更充分地利用Python生态环境来解决问题。

读者对象

本书适读对象包括图像处理工程师、计算机视觉工程师、软件工程师、机器学习工程师,以及其他想要通过使用“实例”熟悉图像处理技术和方法的任何人。虽然阅读本书不要求具备图像处理方面的相关知识,但要理解本书所涵盖的主要概念,读者需要具备一些Python相关的编码经验。

各章内容概述

在第1章中,我们将介绍如何用NumPy、SciPy、scikit-image、OpenCV和Matplotlib等Python库来进行图像处理和变换。通过学习本章内容,读者可以知道如何通过Python代码来执行点变换(对数变换/伽马变换、Gotham滤波器、颜色空间变换和增强亮度/对比度)以及几何变换(螺旋变换、透视变换和单应性变换)。

在第2章中,我们将介绍如何用NumPy、SciPy、scikit-image、OpenCV、MedPy等Python库对图像进行去噪,如使用线性/非线性滤波器、快速傅里叶变换(FFT)和自动编码器。通过学习本章内容,读者可以知道如何实现图像增强技术,如直方图均衡化/匹配、草图绘制/卡通化、金字塔融合/梯度融合和基于零交叉的边缘检测。

在第3章中,我们将介绍如何通过反卷积(逆向、维纳、LMS)滤波器实现图像修复(通过使用NumPy、scikit-image、OpenCV和scikit-learn库)。通过学习本章内容,读者可以知道如何通过图像修复、变分方法和稀疏字典学习实现图像修复,还可以了解如何实现隐写术/隐写分析技术。

在第4章中,我们将介绍如何用NumPy、SciPy、scikit-image和OpenCV等Python库进行二值图像处理(使用数学形态学)。通过学习本章内容,读者可以知道如何实现形态学运算、滤波器和模式匹配,以及如何将其应用于图像分割、指纹图像增强、对象计数和Blob分离。

在第5章中,我们将介绍如何用NumPy、scikit-image、OpenCV等Python库进行图像匹配、配准、拼接。通过学习本章内容,读者可以知道如何使用基于图像扭曲/特征(SIFT/SURF/ORB)的方法和RANSAC算法来实现图像配准技术,还可以了解如何实现全景图创建、人脸变形,以及如何实现基本的图像搜索引擎。

在第6章中,我们将介绍如何用NumPy、scikit-image、OpenCV、Simpletik和DeepLab等Python库来进行图像分割。通过学习本章内容,读者可以知道如何使用基于图的方法/聚类方法、超像素和机器学习算法来实现图像分割技术,还可以了解如何使用DeepLab来实现语义分割。

在第7章中,我们将介绍如何用scikit-learn、OpenCV、TensorFlow、Keras和PyTorch等Python库进行图像分类。通过学习本章内容,读者可以知道如何实现基于深度学习的技术,例如迁移学习/微调,还可以了解如何实现全景图像创建和人脸变形,以及如何实现基于深度学习的适用于手势和交通信号灯的分类技术。

在第8章中,我们将介绍如何用scikit-learn、OpenCV、TensorFlow、Keras和PyTorch等Python库进行图像中的目标检测。通过学习本章内容,读者可以知道如何实现经典的机器学习算法——方向梯度直方图/支持向量机以及深度学习模型,进而检测目标。读者还可以了解如何从图像中实现条形码检测和文本检测。

在第9章中,我们将介绍如何用scikit-learn、OpenCV、dlib、TensorFlow、Keras、PyTorch、DeepFace和FaceNet等Python库在图像中进行人脸检测。读者还可以了解如何用深度学习来实现面部关键点识别以及面部、情绪、性别的识别。

如何更有效地学习本书的内容

理解和运行本书代码,除了需要具备Python和图像处理的基本知识,读者还需要访问一些在线图像数据集以及相关GitHub链接。

对于Windows用户,最好选择安装Anaconda Python 3.5以上版本(本书代码已通过Python 3.7.4测试)以及Jupyter(用于查看/运行笔记)。

本书所有代码均在Windows 10(专业版)系统上完成测试。系统配置为:32GB内存和英特尔(Intel)i7系列处理器。若要在Linux系统上运行本书代码,则需进行少量更改/或不进行更改(根据需要)。

读者需要使用pip 3工具来安装运行代码所必需的Python包。

建议读者使用GPU,以便更加快速地运行涉及使用深度学习进行训练(涉及诸如TensorFlow、Keras和PyTorch等Python库上的训练)的实例。在GPU上运行最好的代码已在配备有NVIDIA Tesla K80 GPU(搭配CUDA 10.1)图形卡的Ubuntu 16.04系统上通过测试。

要理解本书中的概念,读者还需要具备基本的数学知识。

本书中所涉及的软/硬件

操作系统要求

Python 3.7.4

Windows 10系统

Anaconda 2019.10 (py37_0)

Windows 10系统

针对GPU,读者需要配备一块英伟达(NVIDIA)图形卡,或者获取亚马逊(AWS)的 GPU实例或谷歌(Google)Colab的使用权。

Windows 10系统/Linux系统

(Ubuntu 16)

我们建议读者尽可能手动输入本书代码,或者通过GitHub存储库访问本书代码。

如有需要,读者可安装Python 3.7以及必要的第三方Python库。安装Anaconda/Jupyter,并针对每个章节打开相应的笔记,运行每个实例的相应代码。按照每个实例的说明进行其他操作(例如,读者可能需要下载预先训练过的模型或图像数据集)。

在“更多实践”的大部分实例中,我们另外给出了一些练习,供读者测评自己对相应内容的掌握程度。

下载实例代码文件

读者可以登录异步社区,下载本书的实例代码文件。

完成文件下载后,请确保使用以下软件的最新版本解压缩或提取文件夹:

对于Windows系统,请使用WinRAR/7-Zip;

对于macOS,请使用Zipeg/iZip/UnRarX;

对于Linux系统,请使用7-Zip/PeaZip。

读者也可以登录GitHub,搜索“Python-Image-Processing-Cookbook”,查找并下载本书的实例代码文件。如果代码有更新,更新内容将会出现在现有GitHub存储库中。

体例约定

本书所采用的体例约定如下。

CodeInText:表示文本中的代码字、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟URL、用户输入和Twitter句柄。例如:用于在每个图像通道上进行插值操作的函数为bilinear_interpolate()

代码设置如下:

def get_grid_coordinates(points):
  xmin, xmax = np.min(points[:, 0]), np.max(points[:, 0]) + 1
  ymin, ymax = np.min(points[:, 1]), np.max(points[:, 1]) + 1
  return np.asarray([(x, y) for y in range(ymin, ymax)
         for x in range(xmin, xmax)], np.uint32)

所有命令行输入或输出编写如下:

$ pip install mtcnn

黑体:表示新术语、重要的单词或者读者在屏幕上看到的语句。菜单或对话框中的单词会以粗体形式出现在文本中。例如:人脸对齐是一个数据归一化过程,它是许多人脸识别算法的重要预处理步骤。

警告或重要提示以这种图标标注。

提示和技巧以这种图标标注。

体例说明

读者会在本书中发现几个重复出现的标题(准备工作、执行步骤、工作原理、更多实践),各部分的作用如下。

准备工作

这一部分旨在告诉读者可以从实例中得到什么,并且描述了如何设置实例所需要的软件或如何进行初始设置。

执行步骤

这一部分包含遵循实例所需要的步骤。

工作原理

这一部分通常包含针对前一节内容的详细解释。

更多实践

这一部分内容可以帮助读者更多地了解实例,进而拓展性地实践。

第1章 图像处理与变换

通过图像处理与变换,我们可以改善图像的外观。图像处理与变换也可用作更复杂图像处理任务(例如图像分类或图像分割——读者将在后续章节中进一步了解)的预处理步骤。在本章中,读者将学习如何使用不同的Python库(NumPy、SciPy、scikit-image、OpenCV-Python、Mahotas和Matplotlib)来进行图像处理和变换。通过不同的实例,读者将学习如何编写Python代码来实现颜色空间变换、几何变换、透视变换/单应性变换等。

本章将涵盖以下实例:

变换颜色空间(RGB→Lab);

应用仿射变换;

应用透视变换和单应性变换;

基于图像创建铅笔草图;

创建卡通图像;

模拟光艺术/长曝光;

在HSV颜色模型中使用颜色进行目标检测。

1.1  技术要求

为避免在运行代码期间出现错误,请先安装Python 3(例如Python 3.6)和所需的Python库——如果还没有安装。如果使用的是Windows系统,建议安装Anaconda发行版。此外,为了使用Notebook(交互式笔记本应用程序),读者还需要安装Jupyter库。

本书涉及的所有代码以配套资源的形式给出。读者如需要,请登录异步社区网站进行下载,并将存储库复制到本地的工作目录下。每一章会有一个对应的文件夹。该文件夹包含一个带有完整代码(针对每一章的所有实例)的Notebook程序文件;一个名为images的子文件夹,其中包含对应章所需的所有输入图像(以及相关文件);部分文件夹还包含一个名为models的子文件夹,其中包含用于对应章中实例的模型和相关文件。

1.2 变换颜色空间(RGB→Lab)

CIELAB(缩写为“Lab”)颜色空间由3个颜色通道组成,进而将像素颜色表示为3个元组(L、a、b)。其中L通道表示光度/照明度/强度(亮度),a通道和b通道则分别代表“绿-红”和“蓝-黄”颜色分量。此颜色模型将强度与颜色完全分离。该颜色空间独立于设备,并且具有很大的色域。在第一个实例中,读者将看到应如何从RGB颜色空间转换到Lab颜色空间(从Lab颜色空间转换到RGB颜色空间的原理相同),以及这个颜色模型的作用。

1.2.1 准备工作

在这个实例中,我们将一朵花的RGB图像用作输入。首先,使用以下代码导入所需的Python库:

import numpy as np
from skimage.io import imread
from skimage.color import rgb2lab, lab2rgb
import matplotlib.pylab as plt

1.2.2 执行步骤

在这个实例中,读者将看到Lab 颜色空间的一些神奇用法,以及它是如何让某些图像操作变得简单而优雅的。

1.通过将Lab颜色空间的颜色通道设置为零,实现RGB图像到灰度图像的转换

我们使用Lab颜色空间和scikit-image库的函数,按照以下步骤来将RGB图像转换为灰度图像。

(1)读取输入图像。执行从RGB颜色空间到Lab颜色空间的转换:

im = imread('images/flowers.png')
im1 = rgb2lab(im)

(2)将颜色通道(Lab颜色空间的第二通道和第三通道)数值设置为零:

im1[...,1] = im1[...,2] = 0

(3)将图像从Lab颜色空间转换回RGB颜色空间,获得灰度图像:

im1 = lab2rgb(im1)

(4)绘制输入和输出图像(如以下代码所示):

plt.figure(figsize=(20,10))
plt.subplot(121), plt.imshow(im), plt.axis('off'),
plt.title('Original image', size=20)
plt.subplot(122), plt.imshow(im1), plt.axis('off'), plt.title('Grayscale image', size=20)
plt.show()

运行上述代码,输出如图1-1所示。

图1-1

2.通过改变亮度通道,改变图像的亮度

使用Lab颜色空间和scikit-image 库函数,执行以下步骤来更改彩色图像的亮度。

(1)将输入图像从RGB颜色空间转换到Lab颜色空间,并增加第一个通道(L通道)的值(增加50):

im1 = rgb2lab(im)
im1[...,0] = im1[...,0] + 50

(2)将其从Lab颜色空间转换回RGB颜色空间,获得更亮的图像:

im1 = lab2rgb(im1)

(3)类似上述步骤,将图像从RGB颜色空间转换到Lab颜色空间,并仅减少第一个通道值(减少50,如以下代码所示),然后将图像转换回RGB颜色空间,得到一个更暗的图像:

im1 = rgb2lab(im)
im1[...,0] = im1[...,0] -50
im1 = lab2rgb(im1)

运行上述代码,并绘制输入和输出图像,则得到图1-2所示的输出。

图1-2

1.2.3 工作原理

使用scikit-image库color模块的rgb2lab()函数可以将图像从RGB颜色空间转换到Lab颜色空间。

使用scikit-image库color模块的lab2rgb()函数可以将之前被转换的图像从Lab颜色空间转换回RGB颜色空间。

由于颜色通道a和颜色通道b是分离的,依据L通道的亮度,通过将颜色通道值设置为零,能够从Lab空间中的颜色图像获得相应的灰度图像。

无须对颜色通道进行任何操作,仅更改Lab空间中的L通道值(不同于RGB颜色空间,需要更改所有通道值),便可以改变输入颜色图像的亮度。

1.2.4 更多实践

Lab颜色空间还有许多其他用途。例如,在Lab颜色空间中仅需要反转光度通道,便可以获得更自然的反转图像,如下所示:

im1 = rgb2lab(im)
im1[...,0] = np.max(im1[...,0]) -im1[...,0]
im1 = lab2rgb(im1)

运行上述代码,显示输入图像以及在Lab颜色空间和RGB颜色空间中所生成的反转图像,则可以得到图1-3所示的图像。

图1-3

可以看到,Lab颜色空间中的反转图像,比RGB颜色空间中的反转图像显得更加自然。

1.3 应用仿射变换

仿射变换是一种保留了点、直线和平面的几何变换。在变换前平行的线,在变换后仍保持平行。对于图像中的每个像素x,仿射变换可以用映射“x |→Mx+b”表示,其中M是线性变换(矩阵),而b是偏移向量。

在本实例中,我们将使用SciPy库的ndimage模块的affine_transform()函数在图像上实现这样一个转换。

1.3.1 准备工作

首先,导入在灰度图像上实现仿射变换所需要的库和函数:

import numpy as np
from scipy import ndimage as ndi
from skimage.io import imread
from skimage.color import rgb2gray

1.3.2 执行步骤

通过使用SciPy库的ndimage模块的函数,执行以下步骤来对图像应用仿射变换。

1.读取彩色图像,将其转换成灰度图像,获得灰度图像形状:

img = rgb2gray(imread('images/humming.png'))
w, h = img.shape

2.应用恒等变换:

mat_identity = np.array([[1,0,0],[0,1,0],[0,0,1]])
img1 = ndi.affine_transform(img, mat_identity)

3.应用反射变换(沿着x轴):

mat_reflect = np.array([[1,0,0],[0,-1,0],[0,0,1]]) @
np.array([[1,0,0],[0,1,-h],[0,0,1]])
img1 = ndi.affine_transform(img, mat_reflect) # offset=(0,h)

4.缩放变换(沿着x轴缩放0.75倍,沿着y轴缩放1.25倍):

s_x, s_y = 0.75, 1.25
mat_scale = np.array([[s_x,0,0],[0,s_y,0],[0,0,1]])
img1 = ndi.affine_transform(img, mat_scale)

5.将图像逆时针旋转30°。这是一个多步骤组合操作,先将图像移位/居中,应用旋转变换,然后对图像应用逆向移位:

theta = np.pi/6
mat_rotate = np.array([[1,0,w/2],[0,1,h/2],[0,0,1]]) @
np.array([[np.cos(theta),np.sin(theta),0],[np.sin(theta),-np.cos(theta),
0],[0,0,1]]) @ np.array([[1,0,-w/2],[0,1,-h/2],[0,0,1]]) img1 = ndi.affine_transform(img1, mat_rotate)

6.对图像应用剪切变换:

lambda1 = 0.5
mat_shear = np.array([[1,lambda1,0],[lambda1,1,0],[0,0,1]])
img1 = ndi.affine_transform(img1, mat_shear)

7.按顺序将所有变换一并应用于图像:

mat_all = mat_identity @ mat_reflect @ mat_scale @ mat_rotate @
mat_shear
ndi.affine_transform(img, mat_all)

仿射变换操作的每一个矩阵(M)如图1-4所示。

图1-4

1.3.3 工作原理

注意,对于图像而言,x轴是垂直(+ve向下)轴,y轴是水平(+ve从左到右)轴。

使用affine_transform()函数,输出(变换后)图像中位置“o”的像素值是由输入图像中位置“np.dot(matrix, o) + offset”处的像素值来确定的。因此,提供用作函数输入项的矩阵实际上是逆向变换矩阵。

在某些情况下,为了将变换后的图像置于可视区域内,需要使用附加矩阵对图像进行平移。

上述代码演示了如何使用affine_transform()函数来实现不同的仿射变换,例如反射、缩放、旋转和剪切。针对每种变换,需要(使用齐次坐标)提供适当的变换矩阵M(见图1-4)。

通过使用所有变换矩阵的乘积,可以同时执行所有仿射变换的组合(例如,如果想要在完成变换T1之后再执行变换T2,需要使用“输入图像”乘矩阵“TT1”)。

如果依次应用所有变换并且逐个绘制变换后图像,将得到图1-5所示的输出。

图1-5

1.3.4 更多实践

针对上一实例,我们对灰度图像应用affine_transform()函数。把该函数应用于彩色图像也能获得同样的效果,例如通过对每个图像通道同时且独立地应用映射函数。scikit-image图像库还提供了AffineTransform类和PiecewiseAffineTransform类,可供读者实现仿射变换。

1.4 应用透视变换和单应性变换

透视(投影)变换的目标是从两个图像之间的点对应估算单应性矩阵(矩阵H)。由于矩阵的景深(DOF)为8,因此至少需要4对匹配点来计算两幅图像的单应性矩阵。计算单应性矩阵所需要的基本概念如图1-6所示。

幸运的是,读者不需要计算奇异值分解(SVD),并且矩阵H也是通过scikit-image库的transform模块中的ProjectiveTransform()函数自动计算的。在本实例中,我们会用该函数来计算单应性矩阵。

图1-6

1.4.1 准备工作

在该实例中,我们会用到一幅“蜂鸟”图像和一幅“月球上的宇航员”图像。我们还是按照常规的做法,先导入所需要的Python库:

from skimage.transform import ProjectiveTransform
from skimage.io import imread
import numpy as np
import matplotlib.pylab as plt

1.4.2 执行步骤

使用scikit-image库的transform 模块,我们执行以下步骤,来对图像应用投影变换。

1.首先读取源图像,并使用np.zeros()函数创建目标图像:

im_src = (imread('images/humming2.png'))
height, width, dim = im_src.shape
im_dst = np.zeros((height, width, dim))

2.创建一个ProjectiveTransform类的实例:

pt = ProjectiveTransform()

3.要自动估算单应性矩阵H,只需在源图像和目标图像之间提供4对匹配点。在这里,我们将目标图像的4个拐角点和输入蜂鸟图像的4个拐角点作为匹配点,如下所示:

src = np.array([[ 295., 174.],
 [ 540., 146. ],
 [ 400., 777.],
 [ 60., 422.]])
dst = np.array([[ 0., 0.],
 [height-1, 0.],
 [height-1, width-1],
 [ 0., width-1]])

4.获取目标图像中每个像素索引所对应的源图像像素索引:

x, y = np.mgrid[:height, :width]
dst_indices = np.hstack((x.reshape(-1, 1), y.reshape(-1,1)))
src_indices = np.round(pt.inverse(dst_indices), 0).astype(int)
valid_idx = np.where((src_indices[:,0] < height) &
(src_indices[:,1] < width) &
                    (src_indices[:,0] >= 0) & (src_indices[:,1] >=0))
dst_indicies_valid = dst_indices[valid_idx]
src_indicies_valid = src_indices[valid_idx]

5.把像素从源图像复制到目标图像:

im_dst[dst_indicies_valid[:,0],dst_indicies_valid[:,1]] =
im_src[src_indicies_valid[:,0],src_indicies_valid[:,1]]

运行上述代码,输出如图1-7所示。

图1-7

图1-8所示的是“月球上的宇航员”的源图像和显示在画布上的目标图像。通过在源图像(拐角点)和画布上的目标图像(拐角点)之间给出4对匹配点,该操作执行起来非常简单。

图1-8

执行投影变换后的输出图像如图1-9所示。

图1-9

1.4.3 工作原理

在上述两种情况下,输入图像被投影到输出图像的理想位置。为了对图像应用透视变换,我们首先需要创建ProjectiveTransform对象。

我们需要将源图像中的一组4像素位置和相应的目标图像中匹配的4像素位置连同ProjectiveTransform对象实例一起传递给estimate()函数,后者将计算单应性矩阵H(如果单应性矩阵H可通过计算得到,则函数返回True)。

ProjectiveTransform对象上调用inverse()函数,该函数将提供与所有目标像素索引相对应的源像素索引。

1.4.4 更多实践

warp()函数(而不是inverse()函数)可以用于实现单应性/投影变换。

1.5 基于图像创建铅笔草图

基于图像生成草图,实际上就是检测图像的边缘。在本实例中,我们将学习如何使用不同的技术从图像中获取草图,这类技术包括高斯差分(及其扩展版本XDOG)、各向异性扩散和局部遮光(应用高斯模糊+反转+阈值)。

1.5.1 准备工作

让我们先导入以下Python库:

import numpy as np
from skimage.io import imread
from skimage.color import rgb2gray
from skimage import util
from skimage import img_as_float
import matplotlib.pylab as plt
from medpy.filter.smoothing import anisotropic_diffusion
from skimage.filters import gaussian, threshold_otsu

1.5.2 执行步骤

要从图像中创建铅笔草图,我们需要执行以下步骤。

1.定义normalize()函数来实现图像的最小值、最大值归一化:

def normalize(img):
    return (img-np.min(img))/(np.max(img)-np.min(img))

2.实现sketch()函数。该函数以图像及其所提取的图像的边缘作为输入参数:

def sketch(img, edges):
    output = np.multiply(img, edges)
    output[output>1]=1
    output[edges==1]=1
    return output

3.实现edges_with_anisotropic_diffusion()函数。该函数通过各向异性扩散从图像中提取边缘:

def edges_with_anisotropic_diffusion(img, niter=100, kappa=10,
gamma=0.1):
    output = img - anisotropic_diffusion(img, niter=niter, \
            kappa=kappa, gamma=gamma, voxelspacing=None, \
            option=1)
    output[output > 0] = 1
    output[output < 0] = 0
    return output

4.实现sketch_with_dodge()函数和edges_with_dodge2()函数。所实现的函数通过局部遮光操作从图像中提取边缘(代码提供了函数的两个实现版本):

def sketch_with_dodge(img):
 orig = img
 blur = gaussian(util.invert(img), sigma=20)
 result = blur / util.invert(orig)
 result[result>1] = 1
 result[orig==1] = 1
 return result
 
def edges_with_dodge2(img):
 img_blurred = gaussian(util.invert(img), sigma=5)
 output = np.divide(img, util.invert(img_blurred) + 0.001)
 output = normalize(output)
 thresh = threshold_otsu(output)
 output = output > thresh
 return output

5.实现edges_with_DOG()函数。利用该函数,使用高斯差分(DOG)运算来从图像中提取边缘:

def edges_with_DOG(img, k = 200, gamma = 1):
    sigma = 0.5
    output = gaussian(img, sigma=sigma) - gamma*gaussian(img, \
                      sigma=k*sigma)
    output[output > 0] = 1
    output[output < 0] = 0
    return output

6.实现sketch_with_XDOG()函数。利用该函数,使用扩展高斯差分(XDOG)运算从图像中生成草图:

def sketch_with_XDOG(image, epsilon=0.01):
    phi = 10
    difference = edges_with_DOG(image, 200, 0.98).astype(np.uint8)
    for i in range(0, len(difference)):
        for j in range(0, len(difference[0])):
              if difference[i][j] >= epsilon:
                 difference[i][j] = 1
              else:
                 ht = np.tanh(phi*(difference[i][j] - epsilon))
              difference[i][j] = 1 + ht
    difference = normalize(difference)
    return difference

运行上述代码并绘制输入及输出的图像,将得到图1-10所示的输出。

图1-10

1.5.3 工作原理

从前文中可以看到,许多草图绘制技术都是通过模糊图像的边缘(例如,使用高斯滤波器或扩散)并在一定程度上去除细节,然后减去原始图像来得到草图轮廓。

通过调用scikit-image库filter模块中的gaussian()函数来模糊图像。通过调用medpy 库中filter.smoothing模块的anisotropic_diffusion()函数来查找具有各向异性扩散(一种变分方法)的图像边缘。

局部遮光操作(使用np.divide()函数)会将反转的模糊图像从图像中分割出来,通过该操作,将突出显示图像中最醒目的边缘。

1.5.4 更多实践

还有一些其他边缘检测技术可供选择,如通过Canny边缘检测(带有滞后阈值),读者可以尝试从图像中生成草图。读者可以尝试各种算法,并对比使用不同算法所获得的草图有何区别。此外,通过使用以下代码——调用OpenCV-Python库函数,如pencilSketch()sylization()函数,可以生成黑白的和彩色的铅笔草图,以及类似水彩效果的图像:

import cv2
import matplotlib.pylab as plt
src = cv2.imread('images/bird.png')
#dst = cv2.detailEnhance(src, sigma_s=10, sigma_r=0.15)
dst_sketch, dst_color_sketch = cv2.pencilSketch(src, sigma_s=50,
sigma_r=0.05, shade_factor=0.05)
dst_water_color = cv2.stylization(src, sigma_s=50, sigma_r=0.05)

运行上述代码并绘制图像,将得到图1-11所示的输出。

图1-11

1.6 创建卡通图像

在本实例中,我们将介绍如何用图像创建卡通风格的平面纹理图像。同样,有很多方法可以实现这一目的。在本实例中,我们将学习如何使用边缘保持双边滤波器来实现卡通风格的平面纹理图像。

1.6.1 准备工作

让我们先导入以下Python库:

import cv2
import numpy as np
import matplotlib.pylab as plt 

1.6.2 执行步骤

在本实例中,我们将用到OpenCV-Python库的bilateralFilter()函数。我们先对图像进行下采样以创建图像金字塔(更多内容参见第7章),然后重复应用小双边滤波器(用以去除不重要的细节)并将图像上采样到图像原始大小,而后需要应用中值模糊(来使纹理变平),最后使用自适应阈值算法所获得的二值图像来对原始图像添加掩膜。具体步骤如下。

1.读取输入图像,并初始化后面代码所要用到的参数:

img = plt.imread("images/bean.png")
 
num_down = 2 # number of downsampling steps
num_bilateral = 7 # number of bilateral filtering steps
 
w, h, _ = img.shape

2.使用高斯金字塔下采样来减小图像尺寸(并使得后续运算更快):

img_color = np.copy(img)
for _ in range(num_down):
 img_color = cv2.pyrDown(img_color)

3.迭代地应用双边滤波器(使用较小直径值)。其中,参数d表示每个像素的邻域直径,参数sigmaColor表示颜色空间中的滤波器sigma,而参数sigmaSpace则表示坐标空间:

for _ in range(num_bilateral):
 img_color = cv2.bilateralFilter(img_color, d=9, sigmaColor=0.1,
sigmaSpace=0.01)

4.使用上采样将图像放大到原始尺寸:

for _ in range(num_down): 
 img_color = cv2.pyrUp(img_color) 

5.将通过以上步骤所得到的图像转换为输出图像,并使用中值滤波器对图像进行模糊处理:

img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) 
img_blur = cv2.medianBlur(img_gray, 7) 

6.检测并增强图像边缘:

img_edge = cv2.adaptiveThreshold((255*img_blur).astype(np.uint8), \ 
            255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, \ 
            blockSize=9, C=2) 

7.将灰度边缘图像转换回RGB彩色图像,并与 RGB 彩色图像进行按位与(and)计算,得到最终输出的卡通图像:

img_edge = cv2.cvtColor(img_edge, cv2.COLOR_GRAY2RGB) 
img_cartoon = cv2.bitwise_and(img_color, img_edge) 

1.6.3 工作原理

如前所述,在本实例中,我们分别调用了OpenCV-Python库中的bilateralFilter()函数、medianBlur()函数、adaptiveThreshold()函数和bitwise_and()函数。也就是说,先去除图像的弱边缘,然后将图像转换为平面纹理,最后增强图像中的突出边缘。

调用OpenCV-Python库中的bilateralFilter()函数来平滑图像纹理,同时保持图像边缘足够清晰。

参数sigmaColor的值越大,邻域中的像素颜色就会越多地混合在一起。这样,就会在输出图像中产生更大的半等色区域。

参数sigmSpace的值越大,颜色相似的像素之间的相互影响就会越大。

通过对图像下采样,我们就可以创建一个图像金字塔(更多内容参见第7章)。

随后,我们重复应用小双边滤波器来去除不重要的细节,并通过上采样将图像调整为其原始尺寸;最后,应用中值模糊(来使纹理变平),并随后使用自适应阈值算法所获得的二值图像来对原始图像添加掩膜。

运行上述代码,将得到图1-12所示的卡通图像输出。

图1-12

1.6.4 更多实践

请读者使用OpenCV库函数的不同参数值来查看这些参数值对所生成的输出图像的影响。此外,正如前文中所提到的,达到相同效果的方法有多种。例如可尝试使用各向异性扩散来获得平面纹理图像。读者会得到图1-13所示的图像(使用medpy库中的anisotropic_diffusion()函数)。

图1-13

1.7 模拟光艺术/长曝光

长曝光(或光艺术)指的是捕捉到时间流逝效果的照片的拍摄过程。长曝光照片的一些流行应用实例包括如绸缎般光滑的水、高速公路上汽车前灯形成的单一的连续运动照明带。在本实例中,我们将通过平均化视频中的图像帧来模拟长曝光。

1.7.1 准备工作

在本实例中,我们将从视频中提取图像帧,然后平均化图像帧来模拟光艺术。让我们先导入所需要的Python库:

from glob import glob
import cv2
import numpy as np
import matplotlib.pylab as plt

1.7.2 执行步骤

要模拟光艺术/长曝光,我们需要执行以下步骤。

1.实现extract_frames()函数,通过该函数从输入参数(传递给函数的视频中)提取前200(最多)帧图像:

def extract_frames(vid_file):
 vidcap = cv2.VideoCapture(vid_file)
 success,image = vidcap.read()
 i = 1
 success = True
 while success and i <= 200:
  cv2.imwrite('images/exposure/vid_{}.jpg'.format(i), image)
  success,image = vidcap.read()
  i += 1

2.调用上述函数,把从Godafost(冰岛)瀑布视频中所提取的所有帧图像(扩展名为.jpg的文件)保存到exposure文件夹中:

extract_frames('images/godafost.mp4') #cloud.mp4 

3.从exposure文件夹中读取所有扩展名为.jpg的文件,以float类型来读取每个文件的内容,将每个图像分成B、G和R这3个颜色通道;计算颜色通道的当前和;最后,计算颜色通道的平均值:

imfiles = glob('images/exposure/*.jpg')
nfiles = len(imfiles)
R1, G1, B1 = 0, 0, 0
for i in range(nfiles):
 image = cv2.imread(imfiles[i]).astype(float)
 (B, G, R) = cv2.split(image)
 R1 += R
 B1 += B
 G1 += G
R1, G1, B1 = R1 / nfiles, G1 / nfiles, B1 / nfiles

4.合并所得到的颜色通道的平均值,并保存最终输出图像:

final = cv2.merge([B1, G1, R1])
cv2.imwrite('images/godafost.png', final)

图1-14显示了从视频中所提取的一个输入帧。

图1-14

运行上述代码,将得到图1-15所示的一幅长曝光图像。

请注意观察云和瀑布中的连续效果。

图1-15

1.7.3 工作原理

调用OpenCV-Python库中的VideoCapture()函数,通过该函数,我们可以创建一个以视频文件作为输入的VideoCapture对象。然后,使用该对象的read()方法来从视频中捕获图像帧。

通过OpenCV-Python库的imread()imwrite()函数分别从/向磁盘读取/写入图像。

调用cv2.split()函数,通过该函数将RGB图像拆分为单独的颜色通道;调用cv2.merge()函数,通过该函数将单独的颜色通道组合回RGB图像。

1.7.4 更多实践

焦点叠加(也称为扩展景深)是(在图像处理/计算摄影中的)一种技术/技巧,该技术采用多张图像(对同一对象在不同焦距处进行拍摄)作为输入,然后通过组合输入图像来创建一个比任何单个源图像都具有更高景深(DOF)的输出图像。读者可以在Python中模拟焦点叠加技术。以下是一个使用mahotas库对从视频中提取的灰度图像帧实现焦点叠加的例子。

使用Mahotas库来扩展景深

使用mahotas库函数,通过执行以下步骤来实现焦点叠加。

1.通过从夜间高速公路交通视频中提取灰度图像帧来创建图像堆栈:

import mahotas as mh
def create_image_stack(vid_file, n = 200):
 vidcap = cv2.VideoCapture(vid_file)
 success,image = vidcap.read()
 i = 0
 success = True
 h, w = image.shape[:2]
 imstack = np.zeros((n, h, w))
 while success and i < n:
   imstack[i,...] = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
   success,image = vidcap.read()
   i += 1
 return imstack
image = create_image_stack('images/highway.mp4') #cloud.mp4
stack,h,w = image.shape

2.使用mahotas库的sobel()函数,将其计算结果作为聚焦的像素级度量:

focus = np.array([mh.sobel(t, just_filter=True) for t in image])

3.在每个像素位置,选择最佳切片(最大聚焦)并创建最终图像:

best = np.argmax(focus, 0)
image = image.reshape((stack,-1)) # image is now (stack, nr_pixels)
image = image.transpose() # image is now (nr_pixels, stack)
final = image[np.arange(len(image)), best.ravel()] # Select the
right pixel at each location
final = final.reshape((h,w)) # reshape to get final result

图1-16所示的是在图像堆栈中所用到的其中一幅输入图像。

图1-16

由算法实现所生成的最终输出图像如图1-17所示。

图1-17

1.8 在HSV颜色模型中使用颜色进行目标检测

在本实例中,我们将介绍如何利用OpenCV-Python库在HSV颜色空间中使用颜色来进行目标检测。首先,由读者指定一个颜色值范围,然后本实例可以通过该颜色值范围识别和提取读者感兴趣的目标。实例可以更改被检测目标的颜色,甚至可以使所检测到的目标变得透明。

1.8.1 准备工作

在本实例中,我们会用到的输入图像是水族馆中一条橙色的鱼,而(实例)感兴趣的目标就是这条鱼。实例将检测这条鱼,改变它的颜色,并使用HSV空间中鱼的颜色范围来使其变得透明。让我们先导入所需要的Python库:

import cv2 
import numpy as np 
import matplotlib.pylab as plt 

1.8.2 执行步骤

要执行该实例,需要执行以下步骤。

1.读取输入图像和背景图像。将BGR输入图像转换为HSV颜色空间中的图像:

bck = cv2.imread("images/fish_bg.png")
img = cv2.imread("images/fish.png")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

2.通过选择鱼的HSV颜色范围,为鱼创建一个掩膜图像:

mask = cv2.inRange(hsv, (5, 75, 25), (25, 255, 255))

3.使用掩膜图像将鱼图像进行图像分片处理:

imask = mask>0
orange = np.zeros_like(img, np.uint8)
orange[imask] = img[imask]

4.仅通过改变色调通道值(加20)将橙色的鱼的颜色改为黄色,并将图像转换回BGR颜色空间:

yellow = img.copy()
hsv[...,0] = hsv[...,0] + 20
yellow[imask] = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)[imask]
yellow = np.clip(yellow, 0, 255)

5.先在没有鱼的输入图像中提取背景,然后从背景图像中提取出前景对象(鱼)所对应的区域,将这两幅图像叠加起来创建透明鱼图像:

bckfish = cv2.bitwise_and(bck, bck, mask=imask.astype(np.uint8))
nofish = img.copy()
nofish = cv2.bitwise_and(nofish, nofish,
mask=(np.bitwise_not(imask)).astype(np.uint8))
nofish = nofish + bckfish

1.8.3 工作原理

图1-18所示的是一幅用于快速查找颜色的 HSV 色图。x轴表示色调,取值范围为 (0,180);y轴 (1) 表示饱和度,取值范围为 (0,255);y 轴 (2) 表示S =255和V=255时所对应的色调值。要在色图中找到一个特定的颜色,只需查找所对应的H和S值的范围,然后设置V值的范围为(25,255)。实例感兴趣的鱼的橙色可以从(5,75,25)到(25,255,255)的HSV范围中搜索,具体如下所示。

图1-18

调用OpenCV-Python库中的inRange()函数,通过该函数进行颜色检测。函数接收HSV颜色模型的输入图像以及输入图像的颜色范围(需提前确定)作为参数。

cv2.inRange()接收3个参数,分别为输入图像、所检测的颜色的下限值和上限值。该函数会返回一个二值掩膜图像,其中,白色像素代表指定范围内的像素,黑色像素代表指定范围外的像素。

要改变所检测到的鱼的颜色,只需改变色调(颜色)通道值。本实例不涉及饱和度和值通道的修改。

通过OpenCV-Python库中的按位运算提取前景图像/背景图像。

注意:透明鱼的背景图像与鱼图像的背景的颜色略有不同,否则透明鱼就真的消失了(隐形障眼法!)。

运行上述代码并绘制图像,则会得到图1-19所示的输出。

图1-19

注意,在OpenCV-Python库中,RGB颜色空间中的图像以BGR格式存储。如果读者想以适当的颜色显示图像,例如在使用Matplotlib中的imshow()函数(该函数需要RGB格式的图像)显示图像之前,必须使用cv2.cvtColor(image, cv2.COLOR_BGR2RGB)来转换图像格式。

读者服务:

微信扫码关注【异步社区】微信公众号,回复“e58895”获取本书配套资源以及异步社区15天VIP会员卡,近千本电子书免费畅读。

相关图书

深度学习的数学——使用Python语言
深度学习的数学——使用Python语言
动手学自然语言处理
动手学自然语言处理
Web应用安全
Web应用安全
Python高性能编程(第2版)
Python高性能编程(第2版)
图像处理与计算机视觉实践——基于OpenCV和Python
图像处理与计算机视觉实践——基于OpenCV和Python
Python数据科学实战
Python数据科学实战

相关文章

相关课程