Сохранить деку в фрейм csv - PullRequest
0 голосов
/ 14 января 2019

Я работаю с элементами отслеживания видео, используя opencv (в основном подсчет количества элементов после определения порога hsv). У меня есть deque буфер для хранения позиций центроидов. Я выбрал ограниченный буфер 64 (~ 2 секунды на 30 кадрах в секунду, может быть дольше). Моя цель - сохранить данные в файл .csv в таком формате, который я могу легко использовать позже (см. Ниже). Кроме того, я подсчитываю количество обнаруженных регионов. Формат будет выглядеть как

cX  cY  number
444 265   19
444 265   19
444 264   19
444 264   19
...

С cX - центроид в X и cY - центроид в Y самого большого элемента, а также количество обнаруженных областей. Наименование столбцов не является главной целью, хотя было бы неплохо.

Для отображения мне нужно, чтобы центроид был tuple. Я заставляю их расти кадр за кадром, используя appendleft:

center_points = deque(maxlen=64)
object_number = deque(maxlen=64)
iteration_counter = 1

    while True


        # read video frames..
        # do stuff...
        # get contours
            my_cnts = cv2.findContours(...)
        # get largest object
            c = max(my_cnts, key=cv2.contourArea)
            ((x, y), radius) = cv2.minEnclosingCircle(c)
            M = cv2.moments(c)
            big_center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
# count object number as int name it 'num'

center_points.appendleft(big_center)
object_number.appendleft(num)

Теперь, когда буфер заполнен, я хочу сохранить данные в файл):

# Convert to array to save
    # Wait until the iteration number is divisible by the buffer length
    if(iteration_number % 64 == 0):
        print("Saving on iteration..." + str(iteration_number))
        array_to_save = np.array([center_points, object_number]).T


        with open(filename,'a') as outfile:
            np.savetxt(outfile, array_to_save,
                       delimiter=',', fmt='%s')
# Add 1 to the counter
    iteration_number = iteration_number + 1

Задача

Код выше работает и пишет что-то похожее на это:

(444 265) 19
(444 265) 19
(444 264) 19
(444 263) 19

Я хотел бы сделать что-то вроде np.array(center_points) и связать это с object_number. У меня были проблемы с размерами (например, (64,2) и (64) несовместимы). Я пробовал np.append и np.stack, но не могу найти правильный способ форматирования данных.

Иначе, я мог бы сохранить код как есть, но я бы хотел как-то избавиться от скобок в столбцах 1 и 2 и вместо этого сохранить этот объект (безуспешно пытался использовать регулярные выражения на array_to_save). Все три столбца должны быть числовыми или сохранены как строковые, но их можно будет легко найти как числовые позже при чтении.

Обновление

На основании комментариев, которые я пытался

array_to_save = np.concatenate([np.array(center_points), object_number[:, None]])
    TypeError: sequence index must be integer, not 'tuple'

я тоже пробовал

array_to_save = np.concatenate([np.array(center_points), np.array(object_number)[:, None]])
    ValueError: all the input array dimensions except for the concatenation axis must match exactly

Ответы [ 2 ]

0 голосов
/ 16 января 2019

Вы можете concatenate массивы вдоль размера столбца, чтобы создать массив (X, 3) из массива (X, 2) и (X,). Чтобы быть готовыми к объединению, все массивы должны иметь одинаковое количество измерений, и, следовательно, вам необходимо добавить дополнительное измерение в плоский массив object_number: (X,) -> (X, 1). Это можно сделать с помощью object_number[:, np.newaxis] или object_number[:, None]. Полное решение тогда:

np.concatenate([np.array(center_points),
                np.array(object_number)[:, None]], axis=-1)
0 голосов
/ 14 января 2019

Я думаю, что отчасти ваша трудность заключается в том, что np.savetxt() плохо работает с кортежами, содержащимися в массивах numpy. Я разработал некоторый тестовый код, который, я думаю, повторяет ключевые аспекты вашей проблемы и предоставляет решения для них:

import numpy as np
from collections import deque

# Create test data
center_points = deque(maxlen=64)
number = deque(maxlen=64)
for i in range(10):
    big_center = (i*3,i*100)
    center_points.appendleft(big_center)
    number.appendleft(19)

# Write the test data

array_to_save = np.array([center_points,number]).T
print (array_to_save)
with open("test.txt","w") as outfile:
    outfile.write("\n".join([" ".join([str(a[0]),str(a[1]),str(b)]) for a,b in
        array_to_save]))

# Re-read the test data
center_points2 = deque(maxlen=64)
number2 = deque(maxlen=64)

with open("test.txt","r") as infile:
    for line in infile:
        x = [int(xx) for xx in line.split()]
        center_points2.append((x[0],x[1]))
        number2.append(x[2])
    new_array = np.array([center_points2,number2]).T

print (new_array)

При запуске этот код выводит следующее, показывая, что оригинал array_to_save идентичен new_array, который был считан обратно:

[[(27, 900) 19]
 [(24, 800) 19]
 [(21, 700) 19]
 [(18, 600) 19]
 [(15, 500) 19]
 [(12, 400) 19]
 [(9, 300) 19]
 [(6, 200) 19]
 [(3, 100) 19]
 [(0, 0) 19]]
[[(27, 900) 19]
 [(24, 800) 19]
 [(21, 700) 19]
 [(18, 600) 19]
 [(15, 500) 19]
 [(12, 400) 19]
 [(9, 300) 19]
 [(6, 200) 19]
 [(3, 100) 19]
 [(0, 0) 19]]

Файл test.txt выглядит следующим образом:

27 900 19
24 800 19
21 700 19
18 600 19
15 500 19
12 400 19
9 300 19
6 200 19
3 100 19
0 0 19

Код чтения и записи файла в этой версии немного сложнее, чем просто вызов np.savetxt(), но он явно обрабатывает кортежи.

Обновление

В качестве альтернативы, если вы предпочитаете выполнять все манипуляции в массивах numpy, вы можете использовать:

import numpy as np
from collections import deque

# Create test data
center_points = deque(maxlen=64)
number = deque(maxlen=64)
for i in range(10):
    big_center = (i*3,i*100)
    center_points.appendleft(big_center)
    number.appendleft(19)

print (center_points)
print (number)

# Write the test data

x, y = zip(*center_points)
array_to_save = np.array([x,y,number]).T
print (array_to_save)
np.savetxt("test.txt", array_to_save, fmt="%d")

# Re-read the test data
new_array = np.loadtxt("test.txt", dtype=int)
print (new_array)

center_points2 = deque(zip(new_array.T[0],new_array.T[1]),maxlen=64)
number2 = deque(new_array.T[2],maxlen=64)
print (center_points2)
print (number2)

При этом используется подход, описанный в Функция транспонирования / распаковки (обратная сторона zip)? для разделения двух элементов каждого кортежа на два списка, которые затем включаются в список number в один * Массив 1026 *, который можно сохранить с помощью savetxt() и повторно загрузить с помощью loadtxt().

Вызовы print() служат только для иллюстрации того, что данные, которыми заканчивается программа, в точности совпадают с данными, с которых она начала. Они производят следующий вывод:

deque([(27, 900), (24, 800), (21, 700), (18, 600), (15, 500), (12, 400), (9, 300), (6, 200), (3, 100), (0, 0)], maxlen=64)
deque([19, 19, 19, 19, 19, 19, 19, 19, 19, 19], maxlen=64)
[[ 27 900  19]
 [ 24 800  19]
 [ 21 700  19]
 [ 18 600  19]
 [ 15 500  19]
 [ 12 400  19]
 [  9 300  19]
 [  6 200  19]
 [  3 100  19]
 [  0   0  19]]
[[ 27 900  19]
 [ 24 800  19]
 [ 21 700  19]
 [ 18 600  19]
 [ 15 500  19]
 [ 12 400  19]
 [  9 300  19]
 [  6 200  19]
 [  3 100  19]
 [  0   0  19]]
deque([(27, 900), (24, 800), (21, 700), (18, 600), (15, 500), (12, 400), (9, 300), (6, 200), (3, 100), (0, 0)], maxlen=64)
deque([19, 19, 19, 19, 19, 19, 19, 19, 19, 19], maxlen=64)
...