我正在尝试研究一种有效的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
不是很熟悉,所以我可能错了(如果有人在评论中纠正我,我会编辑(。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
插值可能造成的伪影?