多维图像阵列的移动/运行窗口

时间:2017-08-06 06:03:32

标签: python opencv numpy moving-average

我正在努力研究一种高效的numpy解决方案,以便在第四维度上执行一系列彩色图像的运行平均值。目录中的一组彩色图像在循环中读取,我想在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]。批评和建议表示欢迎。

1 个答案:

答案 0 :(得分: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),我确保只使用第一个维度(=不同的图像)进行平均。

  • 默认情况下,运行窗口(或更确切地说是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插值中出现的可能的伪像?