>>> from env_helper import info; info()
页面更新时间: 2024-04-07 16:01:44
运行环境:
    Linux发行版本: Debian GNU/Linux 12 (bookworm)
    操作系统内核: Linux-6.1.0-18-amd64-x86_64-with-glibc2.36
    Python版本: 3.11.2

1.6. python数字图像处理:图像的批量处理

有些时候,我们不仅要对一张图片进行处理,可能还会对一批图片处理。这时候,我们可以通过循环来执行处理,也可以调用程序自带的图片集合来处理。

图片集合函数为:

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))
30

显示结果为 30 , 说明系统自带了 30 张 PNG 的示例图片, 这些图片都读取了出来,放在图片集合coll里。 如果我们想显示其中一张图片,则可以使用索引:

>>> io.imshow(coll[10])
<matplotlib.image.AxesImage at 0x7f14b5e8fc90>
_images/sec06_batch_4_1.png

如果一个文件夹里,我们既存放了一些jpg格式的图片,又存放了一些png格式的图片,现在想把它们全部读取出来,该怎么做呢?

>>> import skimage.io as io
>>> from skimage import data_dir
>>> str=data_dir+'/*.jpg:'+data_dir+'/*.png'
>>> coll = io.ImageCollection(str)
>>> print(len(coll))
33

注意这个地方 data_dir+'/*.jpg:'+data_dir+'/*.png' ,是两个字符串合在一起的。 第一个是 data_dir+'/*.jpg:' , 第二个是 data_dir+'/*.png' 。 合在一起后,中间用冒号来隔开,这样就可以把文件夹下的jpg和png格式的图片都读取出来。 如果还想读取存放在其它地方的图片,也可以一并加进去,只是中间同样用冒号来隔开。

io.ImageCollection() 这个函数省略第二个参数,就是批量读取。 如果我们不是想批量读取,而是其它批量操作,如批量转换为灰度图,那又该怎么做呢?

那就需要先定义一个函数,然后将这个函数作为第二个参数,如:

>>> from skimage import data_dir,io,color
>>>
>>> def convert_gray(f):
>>>     rgb=io.imread(f)
>>>     # return rgb
>>>     return color.rgb2gray(rgb)
>>> data_dir
'/usr/lib/python3/dist-packages/skimage/data'
>>>
>>> str_d=data_dir+'/*.png'
>>> coll = io.ImageCollection(str_d,load_func=convert_gray)
>>> coll
<skimage.io.collection.ImageCollection at 0x7f14ac9d8310>
>>> # io.imshow(coll[10])

在 skimg 中上面结果无法显示,出错信息:

ValueError: the input array must have size 3 along `channel_axis`, got (300, 400)

检索信息,找到: https://stackoverflow.com/questions/70895576/skimage-rgb2gray-giving-errors-the-input-array-must-have-size-3-along?newreg=8b850c2c49c6446a8df71d7908c75e3c

这里给出使用 opencv2 的方法:

>>> import cv2
>>>
>>> image = cv2.imread(data_dir + '/chelsea.png')
>>> grayscale = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
libpng warning: iCCP: known incorrect sRGB profile
>>> import matplotlib.pyplot as plt
>>>
>>> plt.imshow(image) # Original Image
>>> plt.imshow(grayscale, cmap="gray"); # Grayscale Image
_images/sec06_batch_15_0.png

以下是另外一个完整的例子。来自: https://scikit-image.org/docs/stable/auto_examples/color_exposure/plot_rgb_to_gray.html

>>> import matplotlib.pyplot as plt
>>>
>>> from skimage import data
>>> from skimage.color import rgb2gray
>>>
>>> original = data.astronaut()
>>> grayscale = rgb2gray(original)
>>>
>>> fig, axes = plt.subplots(1, 2, figsize=(8, 4))
>>> ax = axes.ravel()
>>>
>>> ax[0].imshow(original)
>>> ax[0].set_title("Original")
>>> ax[1].imshow(grayscale, cmap=plt.cm.gray)
>>> ax[1].set_title("Grayscale")
>>>
>>> fig.tight_layout()
>>> plt.show()
_images/sec06_batch_17_0.png

1.6.1. 读取视频文件

这种批量操作对视频处理是极其有用的,因为视频就是一系列的图片组合。

首先使用 iamgeio 模块来读取磁盘中的视频文件:

>>> import imageio
>>> vid = imageio.get_reader('/cvdata/vtest.avi',  'ffmpeg')
>>> type(vid)
imageio.plugins.ffmpeg.FfmpegFormat.Reader

下面看一下用 skimage 来读取:

>>> def vidread_step(f, step):
>>>     vid = imageio.get_reader(f)
>>>     seq = [v for v in vid.iter_data()]
>>>     return seq[::step]
>>>
>>> ic = io.ImageCollection('/cvdata/vtest.avi', load_func=vidread_step, step=30)[0]

这段代码的意思,就是将这个视频中每隔 30 帧的图片读取出来,放在图片集合中。

>>> import matplotlib.pyplot as plt
>>> plt.imshow(ic[0])
<matplotlib.image.AxesImage at 0x7f14afec8510>
_images/sec06_batch_23_1.png
>>> plt.imshow(ic[20])
<matplotlib.image.AxesImage at 0x7f14afa00550>
_images/sec06_batch_24_1.png

得到图片集合以后,我们还可以将这些图片连接起来,构成一个维度更高的数组,连接图片的函数为:

>>> joined = io.concatenate_images(ic)

带一个参数,就是以上的图片集合,如:

>>> from skimage import data_dir,io,color
>>>
>>> coll = io.ImageCollection('*.png')
>>> # mat=io.concatenate_images(coll)

使用 concatenate_images() 函数的前提是读取的这些图片尺寸必须一致,否则会出错。

我们看看图片连接前后的维度变化:

>>> print(len(ic))      #连接的图片数量
>>> print(ic[0].shape)   #连接前的图片尺寸,所有的都一样
>>>
>>>
>>> mat=io.concatenate_images(joined)
>>> print(mat.shape)  #连接后的数组尺寸
27
(576, 768, 3)
(27, 576, 768, 3)

可以看到,将2个3维数组,连接成了一个4维数组

如果我们对图片进行批量操作后,想把操作后的结果保存起来,也是可以办到的。

例:把系统自带的所有png示例图片,全部转换成256*256的jpg格式灰度图,并进行保存:

改变图片的大小,我们可以使用 tranform 模块的 resize() 函数。

>>> from skimage import data_dir,io,transform,color
>>> import numpy as np
>>>
>>> def convert_gray(f):
>>>     rgb=io.imread(f)    #依次读取rgb图片
>>>
>>>     # 由于不同来源的数据波段或通道不一定是3个,可能会触发异常。
>>>     try:
>>>         gray=color.rgb2gray(rgb)   #将rgb图片转换成灰度图
>>>     except:
>>>         return np.array(
>>>             [[0,1],
>>>              [0,254],
>>>              [0,0] ]
>>>         )
>>>
>>>     dst=transform.resize(gray,(256,256))  #将灰度图片大小转换为256*256
>>>     return dst
>>>
>>> # str='*.png'
>>> str_d=data_dir+'/*.png'
>>> coll = io.ImageCollection(str_d,load_func=convert_gray)
>>> for i in range(len(coll)):
>>>     # io.imsave( 'xx_' + np.str(i)+'.jpg',coll[i])  #循环保存图片
>>>     io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from float64 to uint8. Range [0, 1]. Convert image to uint8 prior to saving to suppress this warning.
WARNING:root:Lossy conversion from float64 to uint8. Range [0, 1]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_2.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
WARNING:root:Lossy conversion from float64 to uint8. Range [0, 1]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_4.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_5.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_6.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
WARNING:root:Lossy conversion from float64 to uint8. Range [0, 1]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_8.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
WARNING:root:Lossy conversion from float64 to uint8. Range [0, 1]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_10.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
WARNING:root:Lossy conversion from float64 to uint8. Range [0, 1]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_12.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
WARNING:root:Lossy conversion from float64 to uint8. Range [0, 1]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_14.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_15.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_16.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_17.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_18.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
WARNING:root:Lossy conversion from float64 to uint8. Range [0, 1]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_20.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_21.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_22.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
WARNING:root:Lossy conversion from float64 to uint8. Range [0, 1]. Convert image to uint8 prior to saving to suppress this warning.
WARNING:root:Lossy conversion from float64 to uint8. Range [0, 1]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_25.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_26.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_27.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.
WARNING:root:Lossy conversion from float64 to uint8. Range [0, 1]. Convert image to uint8 prior to saving to suppress this warning.
/tmp/ipykernel_44368/1922167539.py:25: UserWarning: xx_29.jpg is a low contrast image
  io.imsave( 'xx_' + f'{i}.jpg',coll[i])  #循环保存图片
WARNING:root:Lossy conversion from int64 to uint8. Range [0, 254]. Convert image to uint8 prior to saving to suppress this warning.

同样的方式,我们把开始读取的视频文件保留成单独的图片:

>>> coll = io.ImageCollection('/cvdata/vtest.avi', load_func=vidread_step, step=30)[0]
>>> for i in range(len(coll)):
>>>     io.imsave( f'xx_{i}.jpg',coll[i])  #循环保存图片