Lucas Kanade python numpy实现使用了大量内存

Lucas Kanade python numpy实现使用了大量内存,python,numpy,opticalflow,Python,Numpy,Opticalflow,我正在使用Lucas Kanade方法编写光流脚本,这是一个大学项目。虽然它工作得很好,但有一点我想不出来。它在开始时使用了很少MB的内存,但这一数量每秒钟都会快速增加。当它计算480p电影的1帧时,它使用了大约1GB。当它达到1.9GB时,它会突然停止并停留在那里,即使只停留几个小时 我试着在另一台电脑上运行这个脚本,它“只”使用1GB 这真是奇怪的行为,因为根据我的计算,它应该使用少于100MB的内存 最让我吃惊的是,在脚本计算了一帧后,我打印了垃圾收集器正在查看的对象数量,大约是200万,

我正在使用Lucas Kanade方法编写光流脚本,这是一个大学项目。虽然它工作得很好,但有一点我想不出来。它在开始时使用了很少MB的内存,但这一数量每秒钟都会快速增加。当它计算480p电影的1帧时,它使用了大约1GB。当它达到1.9GB时,它会突然停止并停留在那里,即使只停留几个小时

我试着在另一台电脑上运行这个脚本,它“只”使用1GB

这真是奇怪的行为,因为根据我的计算,它应该使用少于100MB的内存

最让我吃惊的是,在脚本计算了一帧后,我打印了垃圾收集器正在查看的对象数量,大约是200万,然后在强制收集后再次打印,结果完全一样。我等待计算第二帧(同时内存使用量增加了~1GB),然后脚本打印出GC正在监视的对象的数量——与接近200万的数字完全相同。那么这意味着什么呢?那个numpy是用C写的并且有内存泄漏

我真的很想了解这种行为


下面是代码:

虽然它不能解释内存问题,但说得婉转一点,您的实现是次优的。不仅没有充分利用numpy的功能,而且算法的流程在避免重复计算方面也不是很好。我认为您只是在耗尽资源运行您的系统,不是因为python或numpy中出现了问题,而是因为您创建了太多不必要的列表列表

看了之后,我将您的主要功能重写如下:

def lucas_kanade_np(im1, im2, win=2):
    assert im1.shape == im2.shape
    I_x = np.zeros(im1.shape)
    I_y = np.zeros(im1.shape)
    I_t = np.zeros(im1.shape)
    I_x[1:-1, 1:-1] = (im1[1:-1, 2:] - im1[1:-1, :-2]) / 2
    I_y[1:-1, 1:-1] = (im1[2:, 1:-1] - im1[:-2, 1:-1]) / 2
    I_t[1:-1, 1:-1] = im1[1:-1, 1:-1] - im2[1:-1, 1:-1]
    params = np.zeros(im1.shape + (5,)) #Ix2, Iy2, Ixy, Ixt, Iyt
    params[..., 0] = I_x * I_x # I_x2
    params[..., 1] = I_y * I_y # I_y2
    params[..., 2] = I_x * I_y # I_xy
    params[..., 3] = I_x * I_t # I_xt
    params[..., 4] = I_y * I_t # I_yt
    del I_x, I_y, I_t
    cum_params = np.cumsum(np.cumsum(params, axis=0), axis=1)
    del params
    win_params = (cum_params[2 * win + 1:, 2 * win + 1:] -
                  cum_params[2 * win + 1:, :-1 - 2 * win] -
                  cum_params[:-1 - 2 * win, 2 * win + 1:] +
                  cum_params[:-1 - 2 * win, :-1 - 2 * win])
    del cum_params
    op_flow = np.zeros(im1.shape + (2,))
    det = win_params[...,0] * win_params[..., 1] - win_params[..., 2] **2
    op_flow_x = np.where(det != 0,
                         (win_params[..., 1] * win_params[..., 3] -
                          win_params[..., 2] * win_params[..., 4]) / det,
                         0)
    op_flow_y = np.where(det != 0,
                         (win_params[..., 0] * win_params[..., 4] -
                          win_params[..., 2] * win_params[..., 3]) / det,
                         0)
    op_flow[win + 1: -1 - win, win + 1: -1 - win, 0] = op_flow_x[:-1, :-1]
    op_flow[win + 1: -1 - win, win + 1: -1 - win, 1] = op_flow_y[:-1, :-1]
    return op_flow
它使用两个嵌套调用
np.cumsum
和排除包含原则来计算窗口参数。由于每个点要求解的方程组只有2x2,因此它使用Cramer规则将解向量化

为了进行比较,我将您的
lucas\u kanade
函数重命名为
lucas\u kanade\u op
,只需对最后一条语句进行一次更改,即可返回numpy数组:

def lucas_kanade_op(im1, im2, win=2) :
    ...
    return np.array(opfl)
我对这两种方法进行了计时(并检查了它们的输出是否相同),毫无意外,利用numpy提供了巨大的提升:

rows, cols = 100, 100
im1 = np.random.rand(rows, cols)
im2 = np.random.rand(rows, cols)
ans1 = lucas_kanade_op(im1, im2)
ans2 = lucas_kanade_np(im1, im2)
np.testing.assert_almost_equal(ans1,ans2)

import timeit
print 'op\'s time:', timeit.timeit('lucas_kanade_op(im1, im2)',
                                   'from __main__ import lucas_kanade_op, im1, im2',
                                   number=1)
print 'np\'s time:', timeit.timeit('lucas_kanade_np(im1, im2)',
                                   'from __main__ import lucas_kanade_np, im1, im2',
                                   number=1)
这将打印出:

op's time: 5.7419579567
np's time: 0.00256002154425
这是一个x2000的速度提升,对于一个小的100x100图像。我不敢为全尺寸480p图像测试您的方法,但是上面的函数可以在每秒随机854x480阵列上处理大约5次计算,没有任何问题


我建议您以类似于上面建议的方式重写代码,充分利用numpy。将您的完整代码发布到将是一个很好的起点。但是,当你的代码一开始效率很低时,寻找对对象的零散引用是毫无意义的

您是否在64位和32位计算机上尝试过该程序?这可以部分解释两台计算机之间0.9G的差异。