多维图像数组的移动/运行窗口



我正在尝试研究一种有效的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

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

为了在图像(或多个图像(的像素上执行局部操作(例如运行平均值(,使用内核进行卷积通常是一种不错的方法。

以下是在您的情况下如何做到这一点。


生成一些示例数据

我使用以下内容生成了 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')来更具体地说明如何生成文件名列表,允许其他文件位于同一目录中而不会引起问题。

  • 我将所有图像放在形状(image,y,x,c)的单个4D uint8 array中,而不是您使用的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不是很熟悉,所以我可能错了(如果有人在评论中纠正我,我会编辑(。

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

  • 默认情况下,运行窗口(或者更确切地说是kernel(用于计算其中心像素的值。为了更紧密地匹配您的代码,我使用了origin=-1,因此内核计算其中心左侧的像素值。

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

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


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

最新更新