Changelog:

  1. 删掉1.1中所有matplotlib代码,只用skimage
  2. 1.2开头说使用Image.open(),但是前面用的是skimage.io.imread(),改成介绍区别
  3. py2的print --> py3的print()
  4. 基于二值化的循环代码,新增布尔索引实现
  5. 1.3中三几何变换
    1. Image类FLIP_LEFT_RIGHT、ROTATE_180等常量 "is deprecated and will be removed in Pillow 10"
    2. rotate继续使用Image类的rotate()方法
    3. flip分为flip(image)(垂直方向)与mirror(image)(水平),位于ImageOps
  6. 1.6图形绘制
    1. 实心圆空心圆椭圆都归为圆一大类
    2. skimage.draw.circle()(已废弃) --> skimage.draw.disk()
  7. 删掉1.6中重复输出
  8. markdown表格太难画,所以没有表格了
  9. 1.6二subplots()中的免疫组织不好看,所以换成了火箭

=============================

1.1 图像读取、显示与保存¶

一、从外部读取图片并显示¶

读取单张彩色 rgb 图片,使用 skimage.io.imread(fname)函数,fname参数,表示需要读取的文件路径;返回ndarray数组。

显示图片使用 skimage.io.imshow(arr)函数,arr参数,表示需要显示的数组(numpy ndarray)。

In [ ]:
from skimage import io
img=io.imread('dog.jpg')
io.imshow(img)
Out[ ]:
<matplotlib.image.AxesImage at 0x117c71e63e0>

读取单张灰度图片,使用 skimage.io.imread(fname,as_gray=True)函数,第一个参数 fname为图片路径,第二个参数as_gray表示是否当作灰度图, bool 型值,默认为 False。

In [ ]:
from skimage import io
img=io.imread('dog.jpg',as_gray=True)
io.imshow(img)
Out[ ]:
<matplotlib.image.AxesImage at 0x117c783abf0>

二、程序自带图片¶

skimage 程序自带了一些示例图片,如果我们不想从外部读取图片,就可以直接使用这 些示例图片:

In [ ]:
from skimage import io,data
img=data.chelsea()
io.imshow(img)
Out[ ]:
<matplotlib.image.AxesImage at 0x117c75114b0>

图片名对应的就是函数名,如 camera 图片对应的函数名为 camera(). 这些示例图片存放 在 skimage 的安装目录下面,路径名称为 data_dir,我们可以将这个路径打印出来看看:

In [ ]:
from skimage import data_dir
print(data_dir)
d:\Applications\miniconda\envs\dip\lib\site-packages\skimage\data

也就是说,下面两行读取图片的代码效果是一样的:

In [ ]:
from skimage import data_dir,data,io
img1=data.chelsea() #读取 lean 图片
img2=io.imread(data_dir+'/chelsea.png') #读取 lena 图片
io.imshow(img1)
io.imshow(img2)
Out[ ]:
<matplotlib.image.AxesImage at 0x117c7588310>

三、保存图片¶

使用 io 模块的 imsave(fname,arr)函数来实现。第一个参数fname表示保存的路径和名称,第 二个参数arr表示需要保存的数组变量。 保存图片的同时也起到了转换格式的作用。如果读取时图片格式为 jpg 图片,保存为 png 格式,则将图片从 jpg 图片转换为 png 图片并保存。

In [ ]:
from skimage import io,data
img=data.chelsea()
io.imshow(img)
io.imsave('d:/cat.jpg',img)

四、图片信息¶

In [ ]:
from skimage import io,data
img=data.chelsea()
io.imshow(img)
print(type(img)) #显示类型
print(img.shape) #显示尺寸
print(img.shape[0]) #图片宽度
print(img.shape[1]) #图片高度
print(img.shape[2]) #图片通道数
print(img.size) #显示总像素个数
print(img.max()) #最大像素值
print(img.min()) #最小像素值
print(img.mean()) #像素平均值
<class 'numpy.ndarray'>
(300, 451, 3)
300
451
3
405900
231
0
115.30514166050752

1.2 图像中的像素访问¶

前面的一些例子中,我们都是利用skimage.io.imread(fname)函数直接得到ndarray;

还可以 Image.open()来打开一幅图像,然后直接对这个 PIL对象(Image类)进行操作。如果只是简单的操作还可以,但是如果操作稍微复杂一些,就比较吃力了。

因此,通过Image.open()加载完图片后,还得把图片转换成矩阵来进行更加复杂的操作。

python 中利用 numpy 库和 scipy 库来进行各种数据操作和科学计算。我们可以通过 pip 来直接安装这两个库:

pip install numpy

pip install scipy

In [ ]:
# 首先需要导入这些包
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
In [ ]:
img=np.array(Image.open('lena.tif')) #打开图像并转化为数字矩阵
plt.figure("lena")
plt.imshow(img)
plt.axis('off')
plt.show()
print (img.shape)
print (img.dtype )
print (img.size )
print (type(img))
(512, 512, 3)
uint8
786432
<class 'numpy.ndarray'>

RGB图片转换为 array 之后,就变成了一个 rows cols channels 的三维矩阵,因此,我们可以使用img[i,j,k]来访问像素值。

In [ ]:
# 例 1:打开图片,并随机添加一些椒盐噪声
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt

img=np.array(Image.open('lena.tif')) #打开图像并转化为数字矩阵
#随机生成 5000 个椒盐
rows,cols,dims=img.shape
for i in range(5000):
    x=np.random.randint(0,rows)
    y=np.random.randint(0,cols)
    img[x,y,:]=255
plt.figure("beauty")
plt.imshow(img)
plt.axis('off')
plt.show()
In [ ]:
# 例 2:将 lena 图像二值化,像素值大于 128 的变为 1,否则变为 0
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
img=np.array(Image.open('lena.tif').convert('L'))
rows,cols=img.shape
for i in range(rows):
    for j in range(cols):
        if img[i,j] <= 128:
            img[i,j]=0
        else:
            img[i,j]=1
plt.figure("lena")
plt.imshow(img,cmap='gray')
# plt.imshow(img)
plt.axis('off')
plt.show()

如果要对多个像素点进行操作,可以使用数组切片方式访问。切片方式返回的是以指定间隔下标访问 该数组的像素值。下面是有关灰度图像的一些例子:

img[i,:] = im[j,:] # 将第 j 行的数值赋值给第 i 行

img[:,i] = 100 # 将第 i 列的所有数值设为 100

img[:100,:50].sum() # 计算前 100 行、前 50 列所有数值的

img[50:100,50:100] # 50~100 行,50~100 列(不包括第 100 行和第 100 列)

img[i].mean() # 第 i 行所有数值的平均值

img[:,-1] # 最后一列

img[-2,:] (or im[-2]) # 倒数第二行

In [ ]:
# 例 2:将 lena 图像二值化,像素值大于 128 的变为 1,否则变为 0
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
img=np.array(Image.open('lena.tif').convert('L'))
# 利用布尔索引实现
img[img<=128]=0
img[img>128]=1

plt.figure("lena")
plt.imshow(img,cmap='gray')
# plt.imshow(img)
plt.axis('off')
plt.show()

1.3 图像通道\几何变换\裁剪¶

一、图像通道¶

1、 彩色图像转灰度图¶

使用函数 convert()来进行转换,它是图像实例对象的一个方法,接受一个 mode 参数, 用以指定一种色彩模式,mode 的取值可以是如下几种:

· 1 (1-bit pixels, black and white, stored with one pixel per byte)

· L (8-bit pixels, black and white)

· P (8-bit pixels, mapped to any other mode using a colour palette)

· RGB (3x8-bit pixels, true colour)

· RGBA (4x8-bit pixels, true colour with transparency mask)

· CMYK (4x8-bit pixels, colour separation)

· YCbCr (3x8-bit pixels, colour video format)

· I (32-bit signed integer pixels)

· F (32-bit floating point pixels)

In [ ]:
from PIL import Image
import matplotlib.pyplot as plt
img=Image.open('ex.png')
gray=img.convert('L')
plt.title("Gray Beauty")
plt.imshow(gray,cmap='gray')
plt.axis('off')
plt.show()

2、 通道分离与合并¶

In [ ]:
from PIL import Image
import matplotlib.pyplot as plt
img=Image.open('ex.png') #打开图像
gray=img.convert('L') #转换成灰度
r,g,b=img.split() #分离三通道
pic=Image.merge('RGB',(r,g,b)) #合并三通道
plt.figure("beauty")

plt.subplot(2,3,1), plt.title('origin')
plt.imshow(img),plt.axis('off')

plt.subplot(2,3,2), plt.title('gray')
plt.imshow(gray,cmap='gray'),plt.axis('off')

plt.subplot(2,3,3), plt.title('merge')
plt.imshow(pic),plt.axis('off')

plt.subplot(2,3,4), plt.title('r')
plt.imshow(r,cmap='gray'),plt.axis('off')

plt.subplot(2,3,5), plt.title('g')
plt.imshow(g,cmap='gray'),plt.axis('off')

plt.subplot(2,3,6), plt.title('b')
plt.imshow(b,cmap='gray'),plt.axis('off')

plt.show()

二、裁剪图片¶

从原图片中裁剪感兴趣区域(roi),裁剪区域由 4-tuple 决定,该 tuple 中信息为(left, upper, right, lower)。

Pillow 坐标系统的原点(0,0)为图片的左上角。坐标中的数字单位为像素点。

In [ ]:
from PIL import Image
import matplotlib.pyplot as plt
img=Image.open('ex.png') #打开图像
plt.figure("beauty")

plt.subplot(1,2,1), plt.title('origin')
plt.imshow(img),plt.axis('off')
# 左上右下
box=(120,80,800,600)
roi=img.crop(box)
plt.subplot(1,2,2), plt.title('roi')
plt.imshow(roi),plt.axis('off')
plt.show()

有的IDE用 plot 绘制显示出图片后,将鼠标移动到图片上,会在右下角出现当前点的坐标,以及 像素值。

三、几何变换¶

Image 类有 resize()、rotate()和 transpose()方法进行几何变换。

1、图像的缩放和旋转

dst = img.resize((128, 128))

dst = img.rotate(45) # 顺时针角度表示

2、转换图像

Constant below is deprecated and will be removed in Pillow 10

`dst = im.transpose(Image.FLIP_LEFT_RIGHT) #左右互换`

`dst = im.transpose(Image.FLIP_TOP_BOTTOM) #上下互换`

`dst = im.transpose(Image.ROTATE_90) #顺时针旋转`

`dst = im.transpose(Image.ROTATE_180)`

`dst = im.transpose(Image.ROTATE_270)`

transpose()和 rotate()没有性能差别。

但是随着新版本日渐临近transpose大限将至。

In [ ]:
from PIL import Image
import matplotlib.pyplot as plt
img=Image.open('ex.png') #打开图像

plt.subplot(2,3,1), plt.title('origin') 
plt.imshow(img),plt.axis('off')
# resize
img_resize=img.resize([100,100])
plt.subplot(2,3,2), plt.title('resize')
plt.imshow(img_resize),plt.axis('off')

# rotete
img_rotate45=img.rotate(45) 
plt.subplot(2,3,3), plt.title('rotate45')
plt.imshow(img_rotate45),plt.axis('off')

plt.figure("beauty")
plt.show()
<Figure size 640x480 with 0 Axes>

对图像的翻转分别使用ImageOps.flip(img)与ImageOps.mirror(img)。

前者垂直,后者水平。

In [ ]:
from PIL import Image, ImageOps
import matplotlib.pyplot as plt

img=Image.open('ex.png') #打开图像
plt.subplot(2,3,1), plt.title('origin') 
plt.imshow(img),plt.axis('off')
#mirror
img_mirror=ImageOps.mirror(img)
plt.subplot(2,3,2), plt.title('mirror')
plt.imshow(img_mirror),plt.axis('off')
# flip
img_flip=ImageOps.flip(img)
plt.subplot(2,3,4), plt.title('flip')
plt.imshow(img_flip),plt.axis('off')
# flip after mirror
img_flip_after_mirror=ImageOps.flip(img_mirror)
plt.subplot(2,3,5), plt.title('flip af mirror')
plt.imshow(img_flip_after_mirror),plt.axis('off')


plt.figure("beauty")
plt.show()
<Figure size 640x480 with 0 Axes>

1.4 对比度与亮度调整¶

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

gamma 调整¶

原理:I=I^g

对原图像的像素,进行幂运算,得到新的像素值。公式中的 g 就是 gamma 值。

如果 gamma>1, 新图像比原图像暗

如果 gamma<1,新图像比原图像亮

函数格式为:skimage.exposure.adjust_gamma(image, gamma=1)

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

In [ ]:
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)

In [ ]:
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 型值

In [ ]:
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

In [ ]:
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]

输出为[ 0 127 255],即像素最小值由 51 变为 0,最大值由 153 变为 255,整体进行了拉伸,但是数据类型没有变,还是 uint8。

可以通过 img_as_float()函数将 unit8 类型转换为 float 型,实际上还有更简单的方法,就是乘以 1.0。

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

由[51,102,153]变成了[ 51. 102. 153.]

而 float 类型的范围是[0,1],因此对 float 进行 rescale_intensity 调整后,范围变为[0,1],而不是[0,255]。

In [ ]:
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 参数,如:

In [ ]:
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]

如果参数 in_range 的[main,max]范围要比原始像素值的范围[min,max] 大或者小,那就 进行裁剪,如:

In [ ]:
mat=exposure.rescale_intensity(tmp,in_range=(0,102))
print(mat)
[0.5 1.  1. ]

如果一个数组里面有负数,现在想调整到正数,就使用 out_range 参数。如:

In [ ]:
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.5 127. ]

1.5 基本图形的绘制¶

图形包括线条、圆形、椭圆形、多边形等。

在 skimage 包中,绘制图形用的是 draw 模块,不要和绘制图像搞混了。

一、线条¶

skimage.draw.line(r1,c1,r2,c2)

r1,r2: 开始点的行数和结束点的行数

c1,c2: 开始点的列数和结束点的列数

In [ ]:
from skimage import draw,data,io
# import matplotlib.pyplot as plt
img=data.chelsea()
rr, cc =draw.line(1, 150, 270, 250)
img[rr, cc] =255
io.imshow(img)
# plt.imshow(img,plt.cm.gray)
Out[ ]:
<matplotlib.image.AxesImage at 0x117c9cbbca0>

如果想画其它颜色的线条,则可以使用 set_color()函数。

In [ ]:
from skimage import draw,data
import matplotlib.pyplot as plt
img=data.chelsea()
rr, cc =draw.line(1, 150, 270, 250)
draw.set_color(img,[rr,cc],[0,0,255])
plt.imshow(img,plt.cm.gray)
plt.show()

二、圆¶

实心圆

函数格式:`skimage.draw.circle(cy, cx, radius)`

skimage.draw.disk([cy, cx], radius)

[cy,cx] 表示圆心点,radius 表示半径

In [ ]:
from skimage import draw,data
import matplotlib.pyplot as plt
img=data.chelsea()
rr, cc=draw.disk([150,150],50)
draw.set_color(img,[rr,cc],[255,0,0])
plt.imshow(img,plt.cm.gray)
Out[ ]:
<matplotlib.image.AxesImage at 0x117c92613c0>

空心圆

skimage.draw.circle_perimeter(r,c,radius)

r,c是圆心坐标,radius 是半径。

In [ ]:
from skimage import draw,data
import matplotlib.pyplot as plt
img=data.chelsea()
rr, cc=draw.circle_perimeter(150,150,50)
draw.set_color(img,[rr,cc],[255,0,0])
plt.imshow(img,plt.cm.gray)
Out[ ]:
<matplotlib.image.AxesImage at 0x117c92b2f80>

空心椭圆

skimage.draw.ellipse_perimeter(r, c, r_radius, c_radius)

In [ ]:
from skimage import draw,data
import matplotlib.pyplot as plt
img=data.chelsea()
rr, cc=draw.ellipse_perimeter(150, 150, 30, 80)
draw.set_color(img,[rr,cc],[255,0,0])
plt.imshow(img,plt.cm.gray)
Out[ ]:
<matplotlib.image.AxesImage at 0x117c93197b0>

多边形¶

函数格式:skimage.draw.polygon(Y,X)

Y 为多边形顶点的行集合,X 为各顶点的列值集合。

In [ ]:
from skimage import draw,data
import matplotlib.pyplot as plt
import numpy as np
img=data.chelsea()
Y=np.array([10,10,60,60])
X=np.array([200,400,400,200])
rr, cc=draw.polygon(Y,X)#此处只设置了四个顶点,因此是个四边形。
draw.set_color(img,[rr,cc],[255,0,0])
plt.imshow(img,plt.cm.gray)
Out[ ]:
<matplotlib.image.AxesImage at 0x117ce021180>

贝塞儿曲线¶

格式:skimage.draw.bezier_curve(y1,x1,y2,x2,y3,x3,weight)

y1,x1 表示第一个控制点坐标

y2,x2 表示第二个控制点坐标

y3,x3 表示第三个控制点坐标

weight 表示中间控制点的权重,用于控制曲线的弯曲度

In [ ]:
from skimage import draw,data
import matplotlib.pyplot as plt
img=data.chelsea()
rr, cc=draw.bezier_curve(150,50,50,280,260,400,2)
draw.set_color(img,[rr,cc],[255,0,0])
plt.imshow(img,plt.cm.gray)
Out[ ]:
<matplotlib.image.AxesImage at 0x117ce06f820>

1.6 图像的绘制¶

〇、从skimage到plt¶

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

io.imshow(img)

这一行代码的实质是利用 matplotlib 包对图片进行绘制,绘制成功后,返回一个matplotlib 类型的数据。

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

In [ ]:
from skimage import io,data
dst=io.imshow(data.astronaut())
print(type(dst))#可以看到返回的AxesImage来自matplotlib
<class 'matplotlib.image.AxesImage'>

我们也可以直接使用matplotlib这样写:

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

plt.imshow()函数格式为: matplotlib.pyplot.imshow(X, cmap=None)

X: 要绘制的图像或数组。

cmap: 颜色图谱(colormap), 默认绘制为 RGB(A)颜色空间。

In [ ]:
import matplotlib.pyplot as plt
from skimage import data
img=data.astronaut()
plt.imshow(img)
plt.show()

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

import matplotlib.pyplot as plt

也就是说,我们绘图实际上用的是 matplotlib 包的 pyplot 模块。

一、figure()和subplot()¶

用 figure 函数和 subplot 函数分别创建主窗口与子图。

In [ ]:
# 例:分开并同时显示宇航员图片的三个通道
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来创建显示窗口与划分子图

In [ ]:
import matplotlib.pyplot as plt
from skimage import data,color
img = data.rocket()
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 间的参数
plt.show()

直接用 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 工具来创建一块画布,从而在画布上绘制图像。(需安 装 PyQt5 包)

In [ ]:
from skimage import data
from skimage.viewer import ImageViewer
img = data.coins()
viewer = ImageViewer(img)
viewer.show()
d:\Applications\miniconda\envs\dip\lib\site-packages\skimage\viewer\utils\__init__.py:1: UserWarning: Recommended matplotlib backend is `Agg` for full skimage.viewer functionality.
  from .core import *
C:\Users\yh\AppData\Local\Temp\ipykernel_49336\2473475741.py:4: FutureWarning: `viewer` is deprecated and will be removed in 0.20. For alternatives, refer to https://scikit-image.org/docs/stable/user_guide/visualization.html
  viewer = ImageViewer(img)
Out[ ]:
[]