有效计算图像 python 的方差
Calculating variance of an image python efficiently
我正在做一个需要获取图像方差的项目。
目前我正在采取两种方法(两种方法都有效但速度很慢):
- 单独计算每个像素的方差:
这是使用numpy的代码,varianceMatrix是输出
varianceMatrix = np.zeros(im.shape,np.uint8)
w = 1 # the radius of pixels neighbors
ny = len(im)
nx = len(im[0])
for i in range(w,nx-w):
for j in range(w,ny-w):
sampleframe = im[j-w:j+w, i-w:i+w]
variance = np.var(sampleframe)
varianceMatrix[j][i] = int(variance)
return varianceMatrix
- 使用现有的 scipy 函数:
这是scipy函数:
from scipy import ndimage
varianceMatrix = ndimage.generic_filter(im, np.var, size = 3)
scipy 函数更快,但没那么快。我正在寻找一个更好的方法来计算方差。
有什么想法吗???
如果使用ndimage.generic_filter
的方法不够快,你可以在Cython中编写自己的方差计算优化实现。
您可以使用众所周知的 sliding window stride trick 来加速计算。它在不复制数据的情况下将两个 "virtual dimensions" 添加到数组的末尾,然后计算它们的方差。
请注意,在您的代码中,im[j-w:j+w, ..]
遍历索引 j-w,j-w+1,...,j+w-1
,最后一个是独占的,您可能没有这个意思。此外,方差大于 uint8 范围,因此您最终得到整数环绕。
import numpy as np
import time
np.random.seed(1234)
img = (np.random.rand(200, 200)*256).astype(np.uint8)
def sliding_window(a, window, axis=-1):
shape = list(a.shape) + [window]
shape[axis] -= window - 1
if shape[axis] < 0:
raise ValueError("Array too small")
strides = a.strides + (a.strides[axis],)
return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
def sliding_img_var(img, window):
if window <= 0:
raise ValueError("invalid window size")
buf = sliding_window(img, 2*window, 0)
buf = sliding_window(buf, 2*window, 1)
out = np.zeros(img.shape, dtype=np.float32)
np.var(buf[:-1,:-1], axis=(-1,-2), out=out[window:-window,window:-window])
return out
def looping_img_var(im, w):
nx, ny = img.shape
varianceMatrix = np.zeros(im.shape, np.float32)
for i in range(w,nx-w):
for j in range(w,ny-w):
sampleframe = im[j-w:j+w, i-w:i+w]
variance = np.var(sampleframe)
varianceMatrix[j][i] = variance
return varianceMatrix
np.set_printoptions(linewidth=1000, edgeitems=5)
start = time.time()
print(sliding_img_var(img, 1))
time_sliding = time.time() - start
start = time.time()
print(looping_img_var(img, 1))
time_looping = time.time() - start
print("duration: sliding: {0} s, looping: {1} s".format(time_sliding, time_looping))
这里是一个使用 OpenCV 的快速解决方案:
import cv2
def winVar(img, wlen):
wmean, wsqrmean = (cv2.boxFilter(x, -1, (wlen, wlen),
borderType=cv2.BORDER_REFLECT) for x in (img, img*img))
return wsqrmean - wmean*wmean
在我的机器上和以下示例中,winVar()
比 ndimage.generic_filter()
快 2915 倍,比 sliding_img_var()
快 10.8 倍(参见 pv. 的回答):
In [66]: img = np.random.randint(0, 256, (500,500)).astype(np.float)
In [67]: %timeit winVar(img, 3)
100 loops, best of 3: 1.76 ms per loop
In [68]: %timeit ndimage.generic_filter(img, np.var, size=3)
1 loops, best of 3: 5.13 s per loop
In [69]: %timeit sliding_img_var(img, 1)
100 loops, best of 3: 19 ms per loop
结果与 ndimage.generic_filter()
匹配:
In [70]: np.allclose(winVar(img, 3), ndimage.generic_filter(img, np.var, size=3))
Out[70]: True
我正在做一个需要获取图像方差的项目。 目前我正在采取两种方法(两种方法都有效但速度很慢):
- 单独计算每个像素的方差:
这是使用numpy的代码,varianceMatrix是输出
varianceMatrix = np.zeros(im.shape,np.uint8)
w = 1 # the radius of pixels neighbors
ny = len(im)
nx = len(im[0])
for i in range(w,nx-w):
for j in range(w,ny-w):
sampleframe = im[j-w:j+w, i-w:i+w]
variance = np.var(sampleframe)
varianceMatrix[j][i] = int(variance)
return varianceMatrix
- 使用现有的 scipy 函数:
这是scipy函数:
from scipy import ndimage
varianceMatrix = ndimage.generic_filter(im, np.var, size = 3)
scipy 函数更快,但没那么快。我正在寻找一个更好的方法来计算方差。
有什么想法吗???
如果使用ndimage.generic_filter
的方法不够快,你可以在Cython中编写自己的方差计算优化实现。
您可以使用众所周知的 sliding window stride trick 来加速计算。它在不复制数据的情况下将两个 "virtual dimensions" 添加到数组的末尾,然后计算它们的方差。
请注意,在您的代码中,im[j-w:j+w, ..]
遍历索引 j-w,j-w+1,...,j+w-1
,最后一个是独占的,您可能没有这个意思。此外,方差大于 uint8 范围,因此您最终得到整数环绕。
import numpy as np
import time
np.random.seed(1234)
img = (np.random.rand(200, 200)*256).astype(np.uint8)
def sliding_window(a, window, axis=-1):
shape = list(a.shape) + [window]
shape[axis] -= window - 1
if shape[axis] < 0:
raise ValueError("Array too small")
strides = a.strides + (a.strides[axis],)
return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
def sliding_img_var(img, window):
if window <= 0:
raise ValueError("invalid window size")
buf = sliding_window(img, 2*window, 0)
buf = sliding_window(buf, 2*window, 1)
out = np.zeros(img.shape, dtype=np.float32)
np.var(buf[:-1,:-1], axis=(-1,-2), out=out[window:-window,window:-window])
return out
def looping_img_var(im, w):
nx, ny = img.shape
varianceMatrix = np.zeros(im.shape, np.float32)
for i in range(w,nx-w):
for j in range(w,ny-w):
sampleframe = im[j-w:j+w, i-w:i+w]
variance = np.var(sampleframe)
varianceMatrix[j][i] = variance
return varianceMatrix
np.set_printoptions(linewidth=1000, edgeitems=5)
start = time.time()
print(sliding_img_var(img, 1))
time_sliding = time.time() - start
start = time.time()
print(looping_img_var(img, 1))
time_looping = time.time() - start
print("duration: sliding: {0} s, looping: {1} s".format(time_sliding, time_looping))
这里是一个使用 OpenCV 的快速解决方案:
import cv2
def winVar(img, wlen):
wmean, wsqrmean = (cv2.boxFilter(x, -1, (wlen, wlen),
borderType=cv2.BORDER_REFLECT) for x in (img, img*img))
return wsqrmean - wmean*wmean
在我的机器上和以下示例中,winVar()
比 ndimage.generic_filter()
快 2915 倍,比 sliding_img_var()
快 10.8 倍(参见 pv. 的回答):
In [66]: img = np.random.randint(0, 256, (500,500)).astype(np.float)
In [67]: %timeit winVar(img, 3)
100 loops, best of 3: 1.76 ms per loop
In [68]: %timeit ndimage.generic_filter(img, np.var, size=3)
1 loops, best of 3: 5.13 s per loop
In [69]: %timeit sliding_img_var(img, 1)
100 loops, best of 3: 19 ms per loop
结果与 ndimage.generic_filter()
匹配:
In [70]: np.allclose(winVar(img, 3), ndimage.generic_filter(img, np.var, size=3))
Out[70]: True