changelog:
3.1
skimage.color.rgb2grey(rgb) --> graydata.chalsea() --> data.chelsea()3.2
plt.title('resize')应为after resize3.3
str='d:/pic/*.jpg:d:/pic/*.png'不能按照预期运行str=['d:/pic/*.jpg' , 'd:/pic/*.png']np.str is a deprecated alias for the builtin str. To silence this warning, use str by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use np.str_ here.在 skimage 中,一张图片就是一个简单的 numpy 数组,数组的数据类型有很多种,相互之间也可以转换。
这些数据类型及取值范围如下表所示: |Data | type Range | |------|-------------------| |uint8 | 0 to 255| |uint16| 0 to 65535| |uint32| 0 to 232| |float | -1 to 1 or 0 to 1| |int8 | -128 to 127| |int16 | -32768 to 32767| |int32 | -231 to 231 - 1|
# 一张图片的像素值范围是[0,255], 因此默认类型是 unit8, 可用如下代码查看数据类型:
from skimage import io,data
img=data.chelsea()
print(img.dtype.name)
uint8
在上面的表中,特别注意的是 float 类型,它的范围是[-1,1]或[0,1]之间。一张彩色图片转换为灰度图后,它的类型就由 unit8 变成了 float
# unit8 转 float
from skimage import data,img_as_float,color
img=data.chelsea()
print("data.chelsea的格式为:{}".format(img.dtype.name))
dst=img_as_float(img)
print("将其转为float后:{}".format(dst.dtype.name))
gry=color.rgb2gray(img)
print("将其转为灰度图后:{}".format(gry.dtype.name))
data.chelsea的格式为:uint8 将其转为float后:float64 将其转为灰度图后:float64
# float 转 uint8
from skimage import img_as_ubyte
import numpy as np
img = np.array([0, 0.5, 1], dtype=float)
print(img.dtype.name)
dst=img_as_ubyte(img)
print(dst.dtype.name)
float64 uint8
float 转为 unit8,有可能会造成数据的损失,因此会有警告提醒。 除了这两种最常用的转换以外,其实有一些其它的类型转换,如下表: |Function name | Description | |------|------| |img_as_float | Convert to 64-bit floating point. | |img_as_ubyte | Convert to 8-bit uint. | |img_as_uint | Convert to 16-bit uint. | |img_as_int | Convert to 16-bit int |
如前所述,除了直接转换可以改变数据类型外,还可以通过图像的颜色空间转换来改变数据类型。
常用的颜色空间有灰度空间、rgb 空间、hsv 空间和 cmyk 空间。颜色空间转换以后,图片类型都变成了 float 型。
所有的颜色空间转换函数,都放在 skimage 的 color 模块内。
# rgb 转灰度图:
from skimage import io,data,color
img=data.chelsea()
gray=color.rgb2gray(img)
io.imshow(gray)
# 其它的转换,用法都是一样的,列举常用的如下:
# hsv = color.rgb2hsv(img)
# io.imshow(hsv)
# lab= color.rgb2lab(img)
# io.imshow(lab)
# 下面是从其他颜色空间转换至rgb,不能用chelsea
# gray= color.gray2rgb(img)
# gray= color.hsv2rgb(img)
# gray= color.lab2rgb(img)
<matplotlib.image.AxesImage at 0x26b1ce58b50>
实际上,上面的所有转换函数,都可以用一个函数来代替:
skimage.color.convert_colorspace(arr, fromspace, tospace)
表示将 arr 从 fromspace 颜色空间转换到 tospace 颜色空间。
# rgb 转 hsv
from skimage import io,data,color
img=data.chelsea()
hsv=color.convert_colorspace(img,'RGB','HSV')
io.imshow(hsv)
<matplotlib.image.AxesImage at 0x26b1cf18610>
在 color 模块的颜色空间转换函数中,还有一个比较有用的函数是skimage.color.label2rgb(arr), 可以根据标签值对图片进行着色。
以后的图片分类后着色就可以用这个函数。
# 根据灰度值,将图片分成三类,然后用默认颜色对三类进行着色
from skimage import io,data,color
import numpy as np
img=data.astronaut()
gray=color.rgb2gray(img)
labels=gray
labels[labels<0.4]=0
labels[labels>=0.75]=2
labels[((gray>=0.4) & (gray<0.75))]=1
dst=color.label2rgb(labels)
io.imshow(dst)
<matplotlib.image.AxesImage at 0x26b1cf5fd30>
图像的形变与缩放,使用的是 skimage 的 transform 模块,函数比较多,功能齐全。
函数格式为:
skimage.transform.resize(image, output_shape)
image: 需要改变尺寸的图片
output_shape: 新的图片尺寸
from skimage import transform,data
import matplotlib.pyplot as plt
print('将 camera 图片由原来的 512*512 大小,变成了 80*60 大小。')
print('从下图中的坐标尺,我们能够看出来:')
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('after resize')
plt.imshow(dst,plt.cm.gray)
plt.show()
将 camera 图片由原来的 512*512 大小,变成了 80*60 大小。 从下图中的坐标尺,我们能够看出来:
函数格式为:
skimage.transform.rescale(image, scale[, ...])
scale 参数可以是单个 float 数,表示缩放的倍数;也可以是一个 float 型的 tuple,如
[0.2,0.5],表示将行列数分开进行缩放。
from skimage import transform,data
img = data.camera()
print("#图片原始大小:{}".format(img.shape))
print("#缩小为原来图片大小的 0.1 倍:{}".format(transform.rescale(img, 0.1).shape))
print("#缩小为原来图片行数一半,列数四分之一:{}".format(transform.rescale(img, [0.5,0.25]).shape))
print("#放大为原来图片大小的 2 倍:{}".format(transform.rescale(img, 2).shape) )
#图片原始大小:(512, 512) #缩小为原来图片大小的 0.1 倍:(51, 51) #缩小为原来图片行数一半,列数四分之一:(256, 128) #放大为原来图片大小的 2 倍:(1024, 1024)
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)
(512, 512) (512, 512) (699, 699)
<matplotlib.image.AxesImage at 0x26b1f0e2ef0>
以多分辨率来解释图像的一种有效但概念简单的结构就是图像金字塔。图像金字塔最初用于机器视觉和图像压缩,一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低的图像集合。金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。当向金字塔的上层移动时,尺寸和分辨率就降低。
举一个高斯金字塔的应用实例,函数原型为:
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=3)) #产生高斯金字塔图像
#共生成了 log3(512)≈6 幅金字塔图像,加上原始图像共 7 幅,pyramid[0]-pyramid[1]
composite_image = np.ones((rows, cols + cols//3+1, 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 #循环融合 7 幅金字塔图像
i_row += n_rows
plt.imshow(composite_image)
plt.show()
d:\Applications\miniconda\envs\dip\lib\site-packages\skimage\_shared\utils.py:348: RuntimeWarning: Images with dimensions (M, N, 3) are interpreted as 2D+RGB by default. Use `multichannel=False` to interpret as 3D image with last dimension of length 3. return func(*args, **kwargs)
# 除了高斯金字塔外,还有其它的金字塔,如拉普拉斯金字塔:
import numpy as np
import matplotlib.pyplot as plt
from skimage import data,transform,img_as_ubyte
image = data.astronaut() #载入宇航员图片
rows, cols, dim = image.shape #获取图片的行数,列数和通道数
pyramid = tuple(transform.pyramid_laplacian(image,downscale= 2,channel_axis=2)) #产生拉普拉斯金字塔图像
# 共生成了 log2(512)=9 幅金字塔图像,加上原始图像共 10 幅,pyramid[0]-pyramid[1]
# 金字塔图像类型为float64,故区间为[0,1]
composite_image = np.ones((rows, cols + cols//2 + 3, 3),dtype=float) #生成背景
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
i_row += n_rows
#手动将[0,1]变成[0,255]
# 为什么手算和使用img_as_ubyte()效果不同?
plt.imshow((composite_image*255).astype(np.ubyte))
# plt.imshow(img_as_ubyte(composite_image))
plt.show()
有些时候,我们不仅要对一张图片进行处理,可能还会对一批图片处理。
这时候,我们可以通过循环来执行处理,也可以调用程序自带的图片集合来处理。
图片集合函数为:
skimage.io.ImageCollection(load_pattern,load_func=None)
这个函数是放在 io 模块内的,带两个参数,第一个参数 load_pattern, 表示图片组的路径,可以是一个 str 字符串。第二个参数 load_func 是一个回调函数,我们对图片进行批量处理就可以通过这个回调函数实现。回调函数默认为 imread(),即默认这个函数是批量读取图片。
import skimage.io as io
from skimage import data_dir
str=data_dir + '/*.png'
coll = io.ImageCollection(str)
print(len(coll))
# 结果为 23 说明系统自带了 23 张 png 的示例图片,这些图片都读取了出来,放在图片集合 coll 里。
# 如果我们想显示其中一张图片,则可以在后加上一行代码:
io.imshow(coll[10])
23
<matplotlib.image.AxesImage at 0x26b1facf0d0>
如果一个文件夹里,我们既存放了一些 jpg 格式的图片,又存放了一些 png 格式的图片, 现在想把它们全部读取出来,该怎么做呢?
import skimage.io as io
from skimage import data_dir
# str='d:/pic/*.jpg:d:/pic/*.png'
# 根据pull#3928,现在改用lists
str=['./*.png','./*.jpg','./*.tif']
coll = io.ImageCollection(str)
print(len(coll))
3
注意这个地方`'d:/pic/*.jpg:d:/pic/*.png'` ,是两个字符串合在一起的,第一个是`'d:/pic/*.jpg'`, 第二个是`'d:/pic/*.png'` ,合在一起后,中间用冒号来隔开,这样就可以把 d:/pic/文件夹下的jpg 和 png 格式的图片都读取出来。如果还想读取存放在其它地方的图片,也可以一并加进去,只是中间同样用冒号来隔开。
要串合多个字符串,可以将它们保存在一个list当中:
str=['./*.png','./*.jpg','./*.tif']
其中,/前面的.意思是当前目录,因为我把图都放到当前目录中了。
io.ImageCollection()这个函数省略第二个参数,就是批量读取。
如果我们不是想批量读取,而是其它批量操作,如批量转换为灰度图,那又该怎么做呢?
那就需要先定义一个函数,然后将这个函数作为第二个参数,如:
from skimage import data_dir,io,color
def convert_gray(f):
rgb=io.imread(f)
return color.rgb2gray(rgb)
str=data_dir+'/*.png'
coll = io.ImageCollection(str,load_func=convert_gray)
io.imshow(coll[10])
<matplotlib.image.AxesImage at 0x26b1fb5cee0>
这种批量操作对视频处理是极其有用的,因为视频就是一系列的图片组合
from skimage import data_dir,io,color
from matplotlib import pyplot as plt
import imageio
# ImageCollection can be modified to load images
# --from an arbitrary source by specifying a combination of load_pattern and load_func.
# For an ImageCollection ic,
# ic[5] uses load_func(load_pattern[5]) to load the image.
video_file = './rikka.gif'
def vidread_step(f, step):
vid = imageio.get_reader(f)
seq = [v for v in vid.iter_data()]
return seq[::step]
ic = io.ImageCollection(video_file, load_func=vidread_step, step=1)
# 长度必然为1
sub_plots=[221,222,223,224]
fig = plt.figure()
for i in range(4):
plt.subplot(sub_plots[i])
plt.imshow(ic[0][i])
plt.show()
这段代码的意思,就是将rikka.gif 这个视频中每隔1帧的图片读取出来,放在图片集合中。
得到图片集合以后,我们还可以将这些图片连接起来,构成一个维度更高的数组,连接图片的函数为:
skimage.io.concatenate_images(ic)
参数就是图片集合。
需要注意,使用该函数的前提是:读取的这些图片尺寸必须一致,否则会出错。
# 我们看看图片连接前后的维度变化:
from skimage import data_dir,io,color
video_file = './rikka.gif'
def vidread_step(f, step):
vid = imageio.get_reader(f)
seq = [v for v in vid.iter_data()]
return seq[::step]
coll = io.ImageCollection(video_file, load_func=vidread_step, step=1)
print(len(coll)) #连接的图片数量
print(coll[0][0].shape) #连接前的图片尺寸,所有的都一样
mat=io.concatenate_images(coll[0])
print(mat.shape) #连接后的数组尺寸
1 (400, 400, 4) (5, 400, 400, 4)
from PIL import Image, ImageDraw,ImageFont
from matplotlib import pyplot as plt
im = Image.open("./dog.jpg").convert('RGBA')
txt=Image.new('RGBA', im.size, (0,0,0,0))
fnt=ImageFont.truetype("c:/Windows/fonts/Tahoma.ttf", 40)
d=ImageDraw.Draw(txt)
d.text((txt.size[0]-240,txt.size[1]-120), " This is YH's\nwater mark",font=fnt, fill=(255,255,255,255))
out=Image.alpha_composite(im, txt)
plt.imshow(out)
<matplotlib.image.AxesImage at 0x26b2134ee00>