绘制与转化

图像绘制

实际上前面我们就已经用到了图像的绘制,如:

io.imshow(img)

这一行代码的实质是利用 matplotlib 包对图片进行绘制,绘制成功后,返回一个 matplotlib 类型的数据。因此,我们也可以这样写:

import matplotlib.pyplot as plt
plt.imshow(img)

imshow()函数格式为:

matplotlib.pyplot.imshow(X, cmap=None)

其中 X 是要绘制的图像或数组。cmap 是颜色图谱(colormap), 默认绘制为 RGB(A)颜色空间。其它可选的颜色图谱如下列表:

颜色图谱                          描述
autumn--bone-白,x线
cool-洋红
copper-flag---gray-hot---hsv                hsv颜色空间,红--绿---洋红-inferno--jet---magma--pink--plasma                       绿--prism--绿---...-绿模式
spring                             洋红-summer                             绿-viridis-绿-winter-绿

用的比较多的有 gray, jet 等,如:

plt.imshow(image,plt.cm.gray)
plt.imshow(img,cmap=plt.cm.jet)

在窗口上绘制完图片后,返回一个 AxesImage 对象。要在窗口上显示这个对象,我们可以调用 show()函数来进行显示,但进行练习的时候(ipython 环境中),一般我们可以省略 show() 函数,也能自动显示出来。

from skimage import io,data
img=data.astronaut()
dst=io.imshow(img)
print(type(dst))
io.show()

可以看到,类型是’matplotlib.image.AxesImage’。显示一张图片,我们通常更愿意这样写:

import matplotlib.pyplot as plt
from skimage import io,data
img=data.astronaut()
plt.imshow(img)
plt.show()

matplotlib 多窗口

matplotlib 是一个专业绘图的库,相当于 matlab 中的 plot,可以设置多个 figure 窗口,设置 figure 的标题,隐藏坐标尺,甚至可以使用 subplot 在一个 figure 中显示多张图片。一般我们可以这样导入 matplotlib 库:

import matplotlib.pyplot as plt

也就是说,我们绘图实际上用的是 matplotlib 包的 pyplot 模块。用 figure 函数和 subplot 函数分别创建主窗口与子图,分开并同时显示宇航员图片的三个通道:

from skimage import data
import matplotlib.pyplot as plt
img=data.astronaut()
plt.figure(num='astronaut',figsize=(8,8))  #创建一个名为astronaut的窗口,并设置大小

plt.subplot(2,2,1)     #将窗口分为两行两列四个子图,则可显示四幅图片
plt.title('origin image')   #第一幅图片标题
plt.imshow(img)      #绘制第一幅图片

plt.subplot(2,2,2)     #第二个子图
plt.title('R channel')   #第二幅图片标题
plt.imshow(img[:,:,0],plt.cm.gray)      #绘制第二幅图片,且为灰度图
plt.axis('off')     #不显示坐标尺寸

plt.subplot(2,2,3)     #第三个子图
plt.title('G channel')   #第三幅图片标题
plt.imshow(img[:,:,1],plt.cm.gray)      #绘制第三幅图片,且为灰度图
plt.axis('off')     #不显示坐标尺寸

plt.subplot(2,2,4)     #第四个子图
plt.title('B channel')   #第四幅图片标题
plt.imshow(img[:,:,2],plt.cm.gray)      #绘制第四幅图片,且为灰度图
plt.axis('off')     #不显示坐标尺寸

plt.show()   #显示窗口

在图片绘制过程中,我们用 matplotlib.pyplot 模块下的 figure() 函数来创建显示窗口,该函数的格式为:

matplotlib.pyplot.figure(num=None, figsize=None, dpi=None, facecolor=None, edgecolor=None)

所有参数都是可选的,都有默认值,因此调用该函数时可以不带任何参数,其中:

  • num: 整型或字符型都可以。如果设置为整型,则该整型数字表示窗口的序号。如果设置为字符型,则该字符串表示窗口的名称。用该参数来命名窗口,如果两个窗口序号或名相同,则后一个窗口会覆盖前一个窗口。
  • figsize: 设置窗口大小。是一个 tuple 型的整数,如 figsize=(8,8)
  • dpi: 整形数字,表示窗口的分辨率。
  • facecolor: 窗口的背景颜色。
  • edgecolor: 窗口的边框颜色。

用 figure() 函数创建的窗口,只能显示一幅图片,如果想要显示多幅图片,则需要将这个窗口再划分为几个子图,在每个子图中显示不同的图片。我们可以使用 subplot() 函数来划分子图,函数格式为:

matplotlib.pyplot.subplot(nrows, ncols, plot_number)

nrows: 子图的行数。ncols: 子图的列数。plot_number: 当前子图的编号。如 plt.subplot(2,2,1) 则表示将 figure 窗口划分成了 2 行 2 列共 4 个子图,当前为第 1 个子图。我们有时也可以用这种写法:

plt.subplot(221)

两种写法效果是一样的。每个子图的标题可用 title()函数来设置,是否使用坐标尺可用 axis()函数来设置,如:

plt.subplot(221)
plt.title("first subwindow")
plt.axis('off')

subplots

用 subplots 来创建显示窗口与划分子图,除了上面那种方法创建显示窗口和划分子图,还有另外一种编写方法也可以,如下例:

import matplotlib.pyplot as plt
from skimage import data,color

img = data.immunohistochemistry()
hsv = color.rgb2hsv(img)

fig, axes = plt.subplots(2, 2, figsize=(7, 6))
ax0, ax1, ax2, ax3 = axes.ravel()

ax0.imshow(img)
ax0.set_title("Original image")

ax1.imshow(hsv[:, :, 0], cmap=plt.cm.gray)
ax1.set_title("H")

ax2.imshow(hsv[:, :, 1], cmap=plt.cm.gray)
ax2.set_title("S")

ax3.imshow(hsv[:, :, 2], cmap=plt.cm.gray)
ax3.set_title("V")

for ax in axes.ravel():
    ax.axis('off')

fig.tight_layout()  #自动调整subplot间的参数

直接用 subplots()函数来创建并划分窗口。注意,比前面的 subplot()函数多了一个 s,该函数格式为:

matplotlib.pyplot.subplots(nrows=1, ncols=1)

nrows: 所有子图行数,默认为 1。ncols: 所有子图列数,默认为 1。返回一个窗口 figure, 和一个 tuple 型的 ax 对象,该对象包含所有的子图,可结合 ravel()函数列出所有子图,如:

fig, axes = plt.subplots(2, 2, figsize=(7, 6))
ax0, ax1, ax2, ax3 = axes.ravel()

创建了 2 行 2 列 4 个子图,分别取名为 ax0,ax1,ax2 和 ax3, 每个子图的标题用 set_title()函数来设置,如:

ax0.imshow(img)
ax0.set_title("Original image")

如果有多个子图,我们还可以使用 tight_layout()函数来调整显示的布局,该函数格式为:

matplotlib.pyplot.tight_layout(pad=1.08, h_pad=None, w_pad=None, rect=None)

所有的参数都是可选的,调用该函数时可省略所有的参数。

  • pad: 主窗口边缘和子图边缘间的间距,默认为 1.08
  • h_pad, w_pad: 子图边缘之间的间距,默认为 pad_inches
  • rect: 一个矩形区域,如果设置这个值,则将所有的子图调整到这个矩形区域内。

一般调用为:

plt.tight_layout()  #自动调整subplot间的参数

其它方法绘图并显示

除了使用 matplotlib 库来绘制图片,skimage 还有另一个子模块 viewer,也提供一个函数来显示图片。不同的是,它利用 Qt 工具来创建一块画布,从而在画布上绘制图像。

from skimage import data
from skimage.viewer import ImageViewer
img = data.coins()
viewer = ImageViewer(img)
viewer.show()

最后总结一下,绘制和显示图片常用到的函数有:

函数名     功能  调用格式
figure  创建一个显示窗口    plt.figure(num=1,figsize=(8,8)
imshow  绘制图片    plt.imshow(image)
show    显示窗口    plt.show()
subplot     划分子图    plt.subplot(2,2,1)
title   设置子图标题(与subplot结合使用   plt.title('origin image')
axis    是否显示坐标尺     plt.axis('off')
subplots    创建带有多个子图的窗口     fig,axes=plt.subplots(2,2,figsize=(8,8))
ravel   为每个子图设置变量   ax0,ax1,ax2,ax3=axes.ravel()
set_title   设置子图标题与axes结合使用  ax0.set_title('first window')
tight_layout    自动调整子图显示布局  plt.tight_layout()

图像的形变与缩放

图像的形变与缩放,使用的是 skimage 的 transform 模块,函数比较多,功能齐全。

改变图片尺寸 resize

函数格式为:

skimage.transform.resize(image, output_shape)

其中 image 是需要改变尺寸的图片,output_size 是新的图片尺寸。

from skimage import transform,data
import matplotlib.pyplot as plt
img = data.camera()
dst=transform.resize(img, (80, 60))
plt.figure('resize')
plt.subplot(121)
plt.title('before resize')
plt.imshow(img,plt.cm.gray)
plt.subplot(122)
plt.title('before resize')
plt.imshow(dst,plt.cm.gray)
plt.show()

将 camera 图片由原来的 512x512 大小,变成了 80x60 大小。从下图中的坐标尺,我们能够看出来:

缩放图片示范

按比例缩放 rescale

函数格式为:

skimage.transform.rescale(image, scale[, ...])

scale 参数可以是单个 float 数,表示缩放的倍数,也可以是一个 float 型的 tuple,如[0.2,0.5],表示将行列数分开进行缩放:

from skimage import transform,data
img = data.camera()
print(img.shape) #图片原始大小
print(transform.rescale(img, 0.1).shape) #缩小为原来图片大小的0.1
print(transform.rescale(img, [0.5,0.25]).shape) #缩小为原来图片行数一半,列数四分之一
print(transform.rescale(img, 2).shape) #放大为原来图片大小的2倍

结果为:

(512, 512)
(51, 51)
(256, 128)
(1024, 1024)

旋转 rotate

skimage.transform.rotate(image, angle[, ...],resize=False)

angle 参数是个 float 类型数,表示旋转的度数,resize 用于控制在旋转时,是否改变大小,默认为 False。

from skimage import transform,data
import matplotlib.pyplot as plt
img = data.camera()
print(img.shape) #图片原始大小
img1=transform.rotate(img, 60) #旋转90度,不改变大小
print(img1.shape)
img2=transform.rotate(img, 30,resize=True) #旋转30度,同时改变大小
print(img2.shape) plt.figure('resize')
plt.subplot(121)plt.title('rotate 60')
plt.imshow(img1,plt.cm.gray)
plt.subplot(122)
plt.title('rotate 30')
plt.imshow(img2,plt.cm.gray)
plt.show()

显示结果:

图像金字塔

以多分辨率来解释图像的一种有效但概念简单的结构就是图像金字塔。图像金字塔最初用于机器视觉和图像压缩,一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低的图像集合。金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。当向金字塔的上层移动时,尺寸和分辨率就降低。

在此,我们举一个高斯金字塔的应用实例,函数原型为:

skimage.transform.pyramid_gaussian(image, downscale=2)

downscale 控制着金字塔的缩放比例:

import numpy as np
import matplotlib.pyplot as plt
from skimage import data,transform
image = data.astronaut() #载入宇航员图片
rows, cols, dim = image.shape #获取图片的行数,列数和通道数
pyramid = tuple(transform.pyramid_gaussian(image, downscale=2)) #产生高斯金字塔图像#共生成了log(512)=9幅金字塔图像,加上原始图像共10幅,pyramid[0]-pyramid[1]
composite_image = np.ones((rows, cols + cols / 2, 3), dtype=np.double) #生成背景composite_image[:rows, :cols, :] = pyramid[0] #融合原始图像
i_row = 0
for p in pyramid[1:]:
       n_rows, n_cols = p.shape[:2]
       composite_image[i_row:i_row + n_rows, cols:cols + n_cols] = p #循环融合9幅金字塔图像
       i_row += n_rows

plt.imshow(composite_image)
plt.show()

上右图,就是 10 张金字塔图像,下标为 0 的表示原始图像,后面每层的图像行和列变为上一层的一半,直至变为 1。除了高斯金字塔外,还有其它的金字塔,如:

skimage.transform.pyramid_laplacian(image, downscale=2)

对比度与亮度调整

图像亮度与对比度的调整,是放在 skimage 包的 exposure 模块里面。

gamma 调整

原理:I=Ig,对原图像的像素,进行幂运算,得到新的像素值。公式中的 g 就是 gamma 值。如果 gamma>1, 新图像比原图像暗;如果 gamma<1,新图像比原图像亮。函数格式为:

skimage.exposure.adjust_gamma(image, gamma=1)

gamma 参数默认为 1,原像不发生变化 。

from skimage import data, exposure, img_as_float
import matplotlib.pyplot as plt
image = img_as_float(data.moon())
gam1= exposure.adjust_gamma(image, 2) #调暗
gam2= exposure.adjust_gamma(image, 0.5) #调亮plt.figure('adjust_gamma',figsize=(8,8))
plt.subplot(131)plt.title('origin image')
plt.imshow(image,plt.cm.gray)plt.axis('off')
plt.subplot(132)
plt.title('gamma=2')
plt.imshow(gam1,plt.cm.gray)
plt.axis('off')
plt.subplot(133)
plt.title('gamma=0.5')
plt.imshow(gam2,plt.cm.gray)
plt.axis('off')
plt.show()

log 对数调整

这个刚好和 gamma 相反,原理:I=log(I)。

from skimage import data, exposure, img_as_float
import matplotlib.pyplot as plt
image = img_as_float(data.moon())
gam1= exposure.adjust_log(image) #对数调整
plt.figure('adjust_gamma',figsize=(8,8))
plt.subplot(121)plt.title('origin image')
plt.imshow(image,plt.cm.gray)
plt.axis('off')
plt.subplot(122)
plt.title('log')
plt.imshow(gam1,plt.cm.gray)
plt.axis('off')
plt.show()

判断图像对比度是否偏低

函数:is_low_contrast(img),返回一个 bool 型值:

from skimage import data, exposure
image =data.moon()
result=exposure.is_low_contrast(image)
print(result)

输出为 False。

调整强度

函数:

skimage.exposure.rescale_intensity(image, in_range='image', out_range='dtype')

in_range 表示输入图片的强度范围,默认为’image’, 表示用图像的最大/最小像素值作为范围 out_range 表示输出图片的强度范围,默认为’dype’, 表示用图像的类型的最大/最小值作为范围 默认情况下,输入图片的[min,max]范围被拉伸到[dtype.min, dtype.max],如果 dtype=uint8, 那么 dtype.min=0, dtype.max=255

import numpy as np
from skimage import exposure
image = np.array([51, 102, 153], dtype=np.uint8)
mat=exposure.rescale_intensity(image)
print(mat)

输出为[ 0 127 255] 即像素最小值由 51 变为 0,最大值由 153 变为 255,整体进行了拉伸,但是数据类型没有变,还是 uint8 前面我们讲过,可以通过 img_as_float()函数将 unit8 类型转换为 float 型,实际上还有更简单的方法,就是乘以 1.0

import numpy as np
image = np.array([51, 102, 153], dtype=np.uint8)
print(image*1.0)

即由[51,102,153]变成了[ 51. 102. 153.],而float 类型的范围是[0,1],因此对 float 进行 rescale_intensity 调整后,范围变为 [0,1],而不是[ 0,255]:

import numpy as np
from skimage import exposure
image = np.array([51, 102, 153], dtype=np.uint8)
tmp=image*1.0
mat=exposure.rescale_intensity(tmp)
print(mat)

结果为 [ 0. 0.5 1. ],如果原始像素值不想被拉伸,只是等比例缩小,就使用 in_range 参数,如:

import numpy as np
from skimage import exposure
image = np.array([51, 102, 153], dtype=np.uint8)
tmp=image*1.0
mat=exposure.rescale_intensity(tmp,in_range=(0,255))
print(mat)

输出为:[ 0.2 0.4 0.6],即原像素值除以 255,如果参数 in_range 的[main,max]范围要比原始像素值的范围[min,max] 大或者小,那就进行裁剪,如:

mat=exposure.rescale_intensity(tmp,in_range=(0,102))
print(mat)

输出[ 0.5 1. 1. ],即原像素值除以 102,超出 1 的变为 1,如果一个数组里面有负数,现在想调整到正数,就使用 out_range 参数。如:

import numpy as np
from skimage import exposure
image = np.array([-10, 0, 10], dtype=np.int8)
mat=exposure.rescale_intensity(image, out_range=(0, 127))
print(mat)

输出 [0 63 127]。

下一页