Moving/running window 多维图像数组

Moving/running window of a Multi-dimensional image array

我正在尝试开发一种高效的 numpy 解决方案,以对第 4 维的一组彩色图像执行 运行 平均。循环读取目录中的一组彩色图像,我想对 3 个子集进行平均。如果目录中有 n = 5 张彩色图像,我想对 [1,2,3]、[2,3,4]、[3,4,5]、[4,5,1] 和 [ 5,1,2] 从而写入 5 个输出平均图像。

from os import listdir
from os.path import isfile, join
import numpy as np
import cv2
from matplotlib import pyplot as plt

mypath = 'C:/path/to/5_image/dir'
onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))]
img = np.empty(len(onlyfiles), dtype=object)
temp = np.zeros((960, 1280, 3, 3), dtype='uint8')
temp_avg = np.zeros((960, 1280, 3), dtype='uint8')

for n in range(0, len(onlyfiles)):
    img[n] = cv2.imread(join(mypath, onlyfiles[n]))  
for n in range(0, len(img)):
    if (n+2) < len(img)-1:
       temp[:, :, :, 0] = img[n]
       temp[:, :, :, 1] = img[n + 1]
       temp[:, :, :, 2] = img[n + 2]
       temp_avg = np.mean(temp,axis=3)
       plt.imshow(temp_avg)
       plt.show()
    else:
       break

这个脚本绝不是完整的或优雅的。我遇到的问题是在绘制平均图像时,颜色 space 似乎失真并且看起来像 CMKY。我没有考虑最后两个移动 windows [4,5,1] 和 [5,1,2]。欢迎批评和建议。

要对图像的像素(或多个图像)执行局部操作(例如 运行 平均值),convolution with a kernel 通常是一个很好的方法。

以下是针对您的情况如何完成的。


生成一些示例数据

我使用以下方法生成了 10 张包含随机噪声的图像:

for i in range(10):
    an_img = np.random.randint(0, 256, (960,1280,3))
    cv2.imwrite("img_"+str(i)+".png", an_img)


准备图像

这是我加载图片的方式:

# Get target file names
mypath = os.getcwd()   # or whatever path you like
fnames = [f for f in listdir(mypath) if f.endswith('.png')]

# Create an array to hold all the images
first_img = cv2.imread(join(mypath, fnames[0]))
y,x,c = first_img.shape
all_imgs  = np.empty((len(fnames),y,x,c), dtype=np.uint8)

# Load all the images
for i,fname in enumerate(fnames):
    all_imgs[i,...] = cv2.imread(join(mypath, fnames[i]))

一些注意事项:

  • 我使用 f.endswith('.png') 更具体地说明我如何生成文件名列表,允许其他文件位于同一目录中而不会引起问题。

  • 我将所有图像放在单个 4D uint8 array 形状 (image,y,x,c) 中,而不是您使用的 object 数组。这是采用下面的卷积方法所必需的。

  • 我使用第一张图片来获取图片的尺寸,这使得代码更加通用。


通过核卷积执行局部平均

仅此而已。

from scipy.ndimage import uniform_filter
done = uniform_filter(all_imgs, size=(3,0,0,0), origin=-1, mode='wrap')

一些注意事项:

  • 我正在使用 scipy.ndimage 因为它很容易将其卷积过滤器应用于具有多个维度的图像(在您的情况下为 4 个)。对于 cv2,我只知道 cv2.filter2D,据我所知,它没有该功能。但是,我对cv2不是很熟悉,所以我对此可能是错误的(如果有人在评论中纠正我,我会编辑)。

  • size kwarg 指定沿数组的每个维度使用的内核大小。通过使用 (3,0,0,0),我确保只有第一维(=不同的图像)用于平均。

  • 默认情况下,运行 window(或者说 kernel)用于计算其中心像素的值。为了与您的代码更匹配,我使用了 origin=-1,因此内核计算其中心左侧第一个像素的值。

  • 默认情况下,边缘情况(本例中的最后两张图像)由 padding 进行反射处理。您的问题表明您想要的是再次使用第一张图片。这是使用 mode='wrap'.

  • 完成的
  • 默认情况下,过滤器 return 的结果与输入的 dtype 相同,此处为 np.uint8。这可能是可取的,但是您的示例代码会产生浮点数,因此您可能希望过滤器也 return 浮点数,您可以通过简单地更改输入的数据类型来实现,即 done = uniform_filter(all_imgs.astype(np.float), size....


至于绘制平均值时的扭曲颜色 space;我无法重现。您的方法似乎为我的随机噪声示例图像生成了正确的输出(在更正我在对您的问题的评论中指出的问题之后)。也许您可以尝试 plt.imshow(temp_avg, interpolation='none') 来避免 imshow 的插值可能造成的伪造?