Python图像处理库应用
- 0. 前言
- 1. 将 RGB 图像转换为灰度图像算法
- 1.1 算法原理
- 3.2 算法实现
- 2. 使用 PIL 库计算图像差异
- 2.1 算法原理
- 2.2 算法实现
- 3. 使用 Scikit-image 转换图像色彩空间
- 3.1 将 RGB 图像转换至 HSV 色彩空间
- 3.2 将 RGB 图像转换至 YUV 色彩空间
- 4. 用 OpenCV 调整图像的大小
- 5. 使用 Scikit-image 在图像中添加水印
- 6. 使用线性变换改变图像的亮度/对比度
- 6.1 算法原理
- 6.2 算法实现
- 小结
- 系列链接
0. 前言
我们已经学习了使用多种不同的 Python
库实现常见的图像处理,但这些库只是 Python
丰富第三方库的冰山一角,在本节中,我们将继续探索更多 Python
图像处理库处理图像分析问题。
1. 将 RGB 图像转换为灰度图像算法
在本节中,我们将学习实现六种不同的算法,以将 RGB
彩色图像(具有 R
、G
、B
三个颜色通道的图像)转换为灰度图像。我们首先介绍不同算法的原理,然后在 rgb2gray()
函数中实现这些算法。
1.1 算法原理
首先,介绍多种不同的强度 (intensity
) 算法,将 RGB
转换为灰度图像的最简单方法是计算三个颜色通道的平均值:
V
=
(
R
+
G
+
B
)
/
3
V = (R+G+B)/3
V=(R+G+B)/3
或者通过使用 RGB
通道的加权组合来与人类的亮度感知相匹配,加权计算公式如下:
V
=
0.3
R
+
0.59
G
+
0.11
B
V = 0.3R+0.59G+0.11B
V=0.3R+0.59G+0.11B
我们也可以通过获取 RGB
通道的最大值来计算最终的灰度值:
V
=
m
a
x
(
R
,
G
,
B
)
V = max(R, G, B)
V=max(R,G,B)
我们也可以通过亮度 (luster
)来获取最终灰度强度值,其中亮度是色调、亮度和饱和度 (HLS
) 颜色空间中的L通道,亮度是 RGB
中最小和最大通道值的平均值:
V
=
(
m
a
x
(
R
,
G
,
B
)
+
m
i
n
(
R
,
G
,
B
)
)
/
2
V = (max(R,G,B) + min(R,G,B))/2
V=(max(R,G,B)+min(R,G,B))/2
或者,我们可以使用 Lab
色彩空间的 L
通道,将图像从 RGB
空间转换为 Lab
颜色空间,并提取 L
(强度)通道值。
最后,我们也可以使用 RGB
色彩空间的 R
通道,即从 RGB
图像中提取红色通道,等效的,我们也可以提取 G
或 B
通道。
3.2 算法实现
首先,导入所需的库并定义 rgb2gray()
函数,使用该函数实现上述六种计算灰度图像的算法,并返回每种算法的输出灰度图像:
import numpy as np
from skimage.color import rgb2lab
from skimage.io import imread
import matplotlib.pyplot as plt
def rgb2gray(img):
gray_images = {}
gray_images['intensity'] = np.mean(image, axis=2)
gray_images['luminance'] = np.average(image, axis=2, weights=[0.3, 0.59, 0.11])
gray_images['value'] = np.max(image, axis=2)
gray_images['luster'] = (np.max(image, axis=2) + np.min(image, axis=2)) / 2
gray_images['Lab L'] = rgb2lab(img)[...,0]
gray_images['RGB R'] = img[...,0]
return gray_images
读取并绘制原始图像:
image = imread('1.png')
plt.figure(figsize=(5,5))
plt.imshow(image), plt.axis('off'), plt.title('RGB image', size=10)
plt.show()
调用函数 rgb2gray()
,绘制不同算法生成的灰度图像:
gray_images = rgb2gray(image)
i = 1
plt.figure(figsize=(15,10))
plt.gray()
for gray_type in sorted(gray_images):
plt.subplot(2,3,i), plt.imshow(gray_images[gray_type]), plt.axis('off'), plt.title(gray_type, size=10)
i += 1
plt.suptitle('Conerting RGB to GrayScale image with different methods', size=25)
plt.show()
2. 使用 PIL 库计算图像差异
图像差异的相关研究一直受到业界的广泛关注,其具有极大的研究价值,旨在从相似的图像对中定位差异目标。
2.1 算法原理
在实践中,我们通常需要计算两个仅略有不同的图像之间的差异,例如,实现视频压缩的一种方法是存储一帧以及其他帧与该帧的差异,而不是存储所有帧,如果视频帧之间仅略有不同,则该技术可以实现较高的压缩比,从而节省存储空间,或者我们可以利用两张图像之间的差异来得到目标对象的图像。在本节中,我们将学习如何使用 PIL
库函数 difference()
来计算两个图像的差异图像。
2.2 算法实现
首先,导入所需的模块,包括 PIL
模块中的 Image
类用于加载/保存图像以及计算图像差异所需的 difference()
函数和用于图像可视化的 matplotlit
库中 pyplot
模块,模块加载完成后使用 Image
模块中的 open()
函数读取两张输入图像。要计算图像间的差异,两张输入图像的尺寸大小必须相同,为了提高程序的鲁棒性,我们可以使用 resize()
函数调整第二幅图像的尺寸大小,使其与第一幅图像尺寸大小相同,加载完成后可以使用 Image.show()
函数查看加载完成的输入图像:
from PIL.ImageChops import difference
from PIL import Image
from matplotlib import pyplot as plt
# 加载两张图像
im1 = Image.open("1.png")
im2 = Image.open("2.png").resize((im1.width, im1.height))
plt.figure(figsize=(20,20))
plt.subplot(121),plt.imshow(im1),plt.title('image1')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(im2),plt.title('image2')
plt.xticks([]), plt.yticks([])
plt.show()
要计算第二幅图像与第一幅图像之间的差异,可以使用 PIL ImageChop
模块的 difference()
函数计算两个图像的差异图像,最后使用 Image
模块中的 save()
函数保存差异图像:
# 计算两张图像的差异,并将差异图像保存在本地文件系统中
difference(im2, im1).show()
difference(im2, im1).save('D1.png')
3. 使用 Scikit-image 转换图像色彩空间
将图像从一个颜色空间转换为另一个颜色空间是一种非常有用的操作,在多种不同应用程序中都有着重要作用,例如图像分割等。
3.1 将 RGB 图像转换至 HSV 色彩空间
在本节中,我们将介绍以下内容,使用 Scikit-Image.Color
模块的 RGB2HSV()
和 HSV2RGB()
函数将彩色图像从 RGB
转换为 HSV
色彩空间,我们还将研究修改 H(hue)
、S(saturation)
和 V(value)
通道中的值对图像的影响。接下来,我们实现将图像从 RGB
色彩空间转换到 HSV
颜色空间,并返回转换后的图像。
(1) 首先导入所需的库、模块和函数,然后读取输入 RGB
彩色图像:
from skimage.io import imread
from skimage.color import rgb2hsv, hsv2rgb
import numpy as np
import matplotlib.pyplot as plt
(2) 使用 skimage.rgb2hsv()
函数将其转换为 HSV
色彩空间,并且使用 Numpy
的 clip()
函数来确保输出像素值在 [0,1]
区间内:
im = imread("1.png")
im_hsv = np.clip(rgb2hsv(im), 0, 1)
绘制原始输入图像,以及转换后图像的 H
、S
和 V
通道。然后,分别更改图像的 H
、S
和 V
通道,修改后将图像转换回 RGB
色彩空间,以查看修改通道像素值对输出图像的影响。
(3) 使用 Matplotlib
的函数 subplots_adjust()
可以调整子图之间的边距,例如,通过指定子图之间的水平和垂直间距来使图像展示更为紧凑:
plt.figure(figsize=(20,12))
plt.subplots_adjust(0,0,1,0.925,0.05,0.05)
plt.gray()
plt.subplot(231), plt.imshow(im_hsv[...,0]), plt.title('h', size=10), plt.axis('off')
plt.subplot(232), plt.imshow(im_hsv[...,1]), plt.title('s', size=10), plt.axis('off')
plt.subplot(233), plt.imshow(im_hsv[...,2]), plt.title('v', size=10), plt.axis('off')
im_hsv_copy = np.copy(im_hsv)
im_hsv[...,0] /= 4
plt.subplot(234), plt.imshow(np.clip(hsv2rgb(im_hsv), 0, 1)), plt.title('original image with h=h/4', size=10), plt.axis('off')
im_hsv = im_hsv_copy
im_hsv[...,1] /= 3
plt.subplot(235), plt.imshow(np.clip(hsv2rgb(im_hsv), 0, 1)), plt.title('original image with s=s/3', size=10), plt.axis('off')
im_hsv = im_hsv_copy
im_hsv[...,2] /= 5
plt.subplot(236), plt.imshow(np.clip(hsv2rgb(im_hsv), 0, 1)), plt.title('original image with v=v/5', size=10), plt.axis('off')
plt.show()
3.2 将 RGB 图像转换至 YUV 色彩空间
接下来,我们将介绍另一个颜色模型 YUV
,其中通道 Y
代表亮度 (brightness
),u
和 v
通道表示色彩 (color
)。我们将实现使用 color
模块函数将彩色图像从 RGB
空间转换为 YUV
颜色空间。
(1) 首先导入相应库中的必需函数,然后读取 RGB
输入图像,并使用 skimage.Color
的 RGB2YUV()
函数将其转换为 YUV
图像:
from skimage.color import rgb2yuv, yuv2rgb
im = imread("2.png")
im_Yuv = rgb2yuv(im)
(2) 分别绘制 YUV
图像中的亮度 (y
) 和色彩通道 (u
, v
),然后修改 YUV
色彩空间中每个通道的值,再使用 skimage.color.yuv2rgb()
函数将图像转换回 RGB
色彩空间,观察修改不同通道对图像的影响:
plt.figure(figsize=(20,15))
plt.subplots_adjust(0,0,1,0.925,0.05,0.05)
plt.gray()
plt.subplot(231), plt.imshow(im_Yuv[...,0]), plt.title('Y', size=10), plt.axis('off')
plt.subplot(232), plt.imshow(im_Yuv[...,1]), plt.title('u', size=10), plt.axis('off')
plt.subplot(233), plt.imshow(im_Yuv[...,2]), plt.title('v', size=10), plt.axis('off')
im_Yuv_copy = np.copy(im_Yuv)
im_Yuv[...,0] /= 2
plt.subplot(234), plt.imshow(np.clip(yuv2rgb(im_Yuv),0,1)), plt.title('original image with Y=Y/2', size=10), plt.axis('off')
im_Yuv = im_Yuv_copy
im_Yuv[...,1] /= 3
plt.subplot(235), plt.imshow(np.clip(yuv2rgb(im_Yuv),0,1)), plt.title('original image with u=u/3', size=10), plt.axis('off')
im_Yuv = im_Yuv_copy
im_Yuv[...,2] /= 4
plt.subplot(236), plt.imshow(np.clip(yuv2rgb(im_Yuv),0,1)), plt.title('original image with v=v/4', size=10), plt.axis('off')
plt.show()
从上图可以看出,降低 Y
通道值会降低图像的亮度(不会改变颜色),而更改 U
、V
通道会改变图像颜色。
4. 用 OpenCV 调整图像的大小
本节我们介绍使用 OpenCV
和多种插值技术来调整图像大小,cv2.resize()
函数可以用于调整图像大小:
cv2.resize(src, dsize, fx, fy, interpolation)
OpenCV
中常用的插值标志 interpolation
包括:cv2.INTER_NEAREST
、cv2.INTER_LINEAR
、cv2.INTER_AREA
、cv2.INTER_LANCZOS4
和 cv2.INTER_CUBIC
。
当我们缩放图像时,为避免混叠图像伪影需要使用插值技术。不同的插值技术对图像的平滑度具有不同的影响。接下来,我们通过使用不同的插值技术将图像放大四倍(沿垂直和水平尺寸均放大四倍),从而观察使用不同插值技术对图像缩放的影响。
(1) 首先导入所需的库。读取输入图像,指定我们将使用的插值算法:
import cv2
import matplotlib.pylab as plt
im = cv2.imread("1.png")
interps = ['nearest', 'bilinear', 'area', 'lanczos', 'bicubic']
(2) 用每种插值算法调整原始图像(将图像放大 4
倍),并绘制每种插值情况下获得的输出图像:
i = 1
plt.figure(figsize=(18,12))
for interp in [cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_AREA, cv2.INTER_LANCZOS4, cv2.INTER_CUBIC]:
im1 = cv2.resize(im, None, fx=8., fy=8., interpolation = interp) # 4 times
plt.subplot(2,3,i)
plt.imshow(cv2.cvtColor(im1, cv2.COLOR_BGR2RGB))
plt.axis('off')
plt.title(interps[i-1], size=20)
i += 1
print(im.shape, im1.shape)
plt.show()
5. 使用 Scikit-image 在图像中添加水印
在图像中添加版权信息的一种常见方法是在图中添加一个 logo
图像作为水印。在本节中,我们将学习如何将 logo
图像添加到图像中。
(1) 加载所需的库。使用 imread()
函数从磁盘中读取原始图像和 logo
图像:
from skimage.color import rgb2gray, gray2rgb
import numpy as np
import matplotlib.pylab as plt
img1 = imread('1.png').astype(np.uint8)
img2 = imread('logo.jpg').astype(np.uint8)
(2) 使用 skimage.color
的 rgb2gray()
函数将 logo
图像转换为灰度,然后应用阈值算法。将 logo
图像用作掩码图像,然后使用感兴趣区域作为要插入 logo
的位置:
rows, cols, _ = img2.shape
roi = img1[0:rows, 0:cols]
img2gray = (255*rgb2gray(img2)).astype(np.uint8)
mask = 255*(img2gray < 150) # cv2.threshold(img2gray, 10, 255, cv2.THRESH_BINARY)
mask_inv = np.invert(mask) # cv2.bitwise_not(mask)
mask_inv = mask_inv.astype(np.uint8)
img1_bg = np.bitwise_and(roi, gray2rgb(mask_inv)) # cv2.bitwise_and(roi,roi,mask = mask_inv)
img2_fg = np.bitwise_and(img2, gray2rgb(mask)) # cv2.bitwise_and(img2,img2,mask = mask)
(3) 最后,对前景图像和背景图像求和以获取修改后的感兴趣区域,并将其分配给原始图像中相应位置以获取水印图像:
dst = img1_bg + img2_fg
img1[0:rows, 0:cols ] = dst
plt.figure(figsize=(20,20))
plt.imshow(img1)
plt.axis('off')
plt.show()
6. 使用线性变换改变图像的亮度/对比度
对比度、亮度增强技术是两种非常常见技术,这些技术是在许多图像处理任务(例如,图像分类)中使用预处理步骤。在本节中,我们将学习如何使用 OpenCV
库来修改图像的对比度和亮度。
6.1 算法原理
乘法和加法是两种常用的图像操作运算,使用这两种运算可以组成基本线性变换:
g
(
x
)
=
α
f
(
x
)
+
β
g(x)=αf(x)+β
g(x)=αf(x)+β,其中常数参数
α
>
0
α>0
α>0 和
β
β
β 通常分别称为对比度(增益)和亮度(偏置)参数。可以将
f
(
x
)
f(x)
f(x) 视为源图像像素,而
g
(
x
)
g(x)
g(x) 则是输出图像像素,因此,我们可以将表达式改写为
g
(
i
,
j
)
=
α
⋅
f
(
i
,
j
)
+
β
g(i,j)=α·f(i,j)+β
g(i,j)=α⋅f(i,j)+β,其中
i
i
i 和
j
j
j 表示像素位于第
i
i
i 行和第
j
j
j 列中。在本节中,我们将学习如何使用 OpenCV
的 convertscaleabs()
实现基本线性变换,并观察其对图像的影响,该函数用法如下:
cv2.convertScaleAbs(src, alpha, beta)
在输入图像的每个通道上,该函数顺序执行三个操作,缩放,计算绝对值,转换为无符号的 8
位类型(对于输入图像
s
r
c
src
src,返回的输出图像是
s
r
c
∗
a
l
p
h
a
+
b
e
t
a
src*alpha+beta
src∗alpha+beta)。
幂转换也称为 gamma
校正,当
γ
>
1
γ>1
γ>1 时图像将转移到频谱的黑色端,而在
γ
<
1
γ<1
γ<1 时会使图像显得偏淡,使用 LUT()
函数可以将图像的查找表更改为具有幂转换的新图像,该用法函数如下:
cv2.LUT(src, lut)
6.2 算法实现
(1) 首先导入所需的库和函数,并加载输出图像,然后使用 OpenCV
函数定义函数 basic_linear_transform()
和 gamma_correction()
分别实现对比度修改和伽马校正:
import cv2
import numpy as np
import matplotlib.pylab as plt
alpha, beta, gamma = 1, 0, 1
def basic_linear_transform(img, alpha, beta):
return cv2.convertScaleAbs(img, alpha=alpha, beta=beta)
def gamma_correction(img, gamma):
lookup_table = np.empty((1,256), np.uint8)
for i in range(256):
lookup_table[0,i] = np.clip(pow(i / 255.0, gamma) * 255.0, 0, 255)
return cv2.LUT(img, lookup_table)
image = cv2.imread('1.png')
(2) 用不同的 alpha
和 gamma
值调用函数 basic_linear_transform()
以改变输入图像的亮度,并绘制输出图像:
plt.figure(figsize=(20,20))
i = 1
for alpha in [0.25, 0.5, 1, 1.5, 2.5]:
for beta in [0, 0.5, 1, 1.5, 2]:
image_corrected = basic_linear_transform(image, alpha, beta)
plt.subplot(5,5,i), plt.imshow(cv2.cvtColor(image_corrected, cv2.COLOR_BGR2RGB)), plt.axis('off')
plt.title(r'$\alpha$={:.2f}, $\beta$={:.2f}'.format(alpha, beta), size=10)
i += 1
plt.suptitle('Basic linear transform to change brightness', size=20)
plt.show()
(3) 用不同的 gamma
参数值调用函数 gamma_correction()
,并绘制输出图像:
plt.figure(figsize=(20,20))
i = 1
for gamma in np.linspace(0, 2, 16):
image_gamma_corrected = gamma_correction(image, gamma)
plt.subplot(4,4,i), plt.imshow(cv2.cvtColor(image_gamma_corrected, cv2.COLOR_BGR2RGB)), plt.axis('off')
plt.title(r'$\gamma$={:.2f}'.format(gamma), size=10)
i += 1
plt.suptitle('Gamma correction', size=20)
plt.show()
小结
在本节中,我们介绍了多个用于图像处理的流行 Python
第三方库,利用这些图像处理库我们可以快速执行基本的图像变换操作。本节我们重点介绍多种可以将 RGB
图像转换为灰度图像算法、介绍了如何使用 PIL
库计算图像间的差异、使用 Scikit-image
转换图像色彩空间以及在图像中添加水印、同时介绍了如何使用 OpenCV
库指定不同的插值算法调整图像大小、最后我们还介绍了如何利用线性变换改变图像的亮度/对比度。
系列链接
Python图像处理【1】图像与视频处理
Python图像处理【2】探索Python图像处理库