Ответ 1
Хотя это не объясняет проблемы с вашей памятью, ваша реализация, мягко говоря, неоптимальная. Мало того, что вы не используете numpy для своих полных возможностей, но поток вашего алгоритма также не очень хорош, чтобы избежать повторных вычислений. Я думаю, что вы просто используете свою систему вне ресурсов, а не потому, что что-то не так в python или numpy, а потому, что вы создаете слишком много лишних списков списков списков...
После просмотра записи в wikipedia по алгоритму Лукаса-Канаде я переписал вашу основную функцию следующим образом:
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, она использует правило Крамера для векторизации решения.
Для сравнения, я переименовал вашу функцию lucas_kanade
как lucas_kanade_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\ time:', timeit.timeit('lucas_kanade_op(im1, im2)',
'from __main__ import lucas_kanade_op, im1, im2',
number=1)
print 'np\ time:', timeit.timeit('lucas_kanade_np(im1, im2)',
'from __main__ import lucas_kanade_np, im1, im2',
number=1)
Это выдает:
op time: 5.7419579567
np time: 0.00256002154425
Для увеличения скорости x2000 для небольшого изображения 100x100. Я не осмелился проверить ваш подход на полноразмерное изображение 480p, но вышеприведенная функция может обрабатывать около 5 вычислений на случайном массиве 854x480 в секунду без каких-либо проблем.
Я бы порекомендовал вам переписать свой код таким же образом, как это было предложено выше, максимально используя numpy. Отправкой полного кода в Обзор кода было бы хорошей отправной точкой. Но на самом деле нет смысла искать блуждающие ссылки на объекты, когда ваш код настолько неэффективен для начала!