Nếu bạn chỉ muốn các vùng liền kề (bán), đã có một cách triển khai dễ dàng trong mô-đun ndimage.morphology của Python: SciPy . Đây là một thao tác hình thái học khá phổ biến .
Về cơ bản, bạn có 5 bước:
def find_paws(data, smooth_radius=5, threshold=0.0001):
data = sp.ndimage.uniform_filter(data, smooth_radius)
thresh = data > threshold
filled = sp.ndimage.morphology.binary_fill_holes(thresh)
coded_paws, num_paws = sp.ndimage.label(filled)
data_slices = sp.ndimage.find_objects(coded_paws)
return object_slices
Làm mờ dữ liệu đầu vào một chút để đảm bảo các bàn chân có dấu chân liên tục. (Sẽ hiệu quả hơn nếu chỉ sử dụng một nhân lớn hơn ( structure
kwarg cho các scipy.ndimage.morphology
chức năng khác nhau ) nhưng điều này không hoàn toàn hoạt động bình thường vì một số lý do ...)
Đặt ngưỡng cho mảng để bạn có một mảng boolean ở những nơi mà áp suất vượt quá một số giá trị ngưỡng (tức là thresh = data > value
)
Lấp các lỗ bên trong để bạn có các vùng sạch hơn ( filled = sp.ndimage.morphology.binary_fill_holes(thresh)
)
Tìm các vùng tiếp giáp riêng biệt ( coded_paws, num_paws = sp.ndimage.label(filled)
). Điều này trả về một mảng với các vùng được mã hóa bằng số (mỗi vùng là vùng liền kề của một số nguyên duy nhất (1 tối đa số bàn chân) với các số không ở mọi nơi khác)).
Cách ly các vùng tiếp giáp bằng cách sử dụng data_slices = sp.ndimage.find_objects(coded_paws)
. Thao tác này trả về danh sách các bộ giá trị slice
đối tượng, vì vậy bạn có thể lấy vùng dữ liệu cho mỗi bộ với [data[x] for x in data_slices]
. Thay vào đó, chúng ta sẽ vẽ một hình chữ nhật dựa trên những lát cắt này, việc này sẽ mất nhiều công hơn một chút.
Hai hoạt ảnh bên dưới hiển thị dữ liệu mẫu về "Các bàn chân chồng chéo" và "Các bàn chân được nhóm" của bạn. Phương pháp này dường như đang hoạt động hoàn hảo. (Và đối với bất kỳ giá trị nào của nó, điều này chạy trơn tru hơn nhiều so với các ảnh GIF bên dưới trên máy của tôi, vì vậy thuật toán phát hiện chân khá nhanh ...)
Đây là một ví dụ đầy đủ (bây giờ có giải thích chi tiết hơn nhiều). Phần lớn việc này là đọc đầu vào và tạo hoạt ảnh. Việc phát hiện con tốt chỉ là 5 dòng mã.
import numpy as np
import scipy as sp
import scipy.ndimage
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
def animate(input_filename):
"""Detects paws and animates the position and raw data of each frame
in the input file"""
infile = paw_file(input_filename)
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
fig.suptitle(input_filename)
im = ax.imshow(infile.next()[1])
rects = [Rectangle((0,0), 1,1, fc='none', ec='red') for i in range(4)]
[ax.add_patch(rect) for rect in rects]
title = ax.set_title('Time 0.0 ms')
for time, frame in infile:
paw_slices = find_paws(frame)
[rect.set_visible(False) for rect in rects]
for slice, rect in zip(paw_slices, rects):
dy, dx = slice
rect.set_xy((dx.start, dy.start))
rect.set_width(dx.stop - dx.start + 1)
rect.set_height(dy.stop - dy.start + 1)
rect.set_visible(True)
title.set_text('Time %0.2f ms' % time)
im.set_data(frame)
im.set_clim([frame.min(), frame.max()])
fig.canvas.draw()
def find_paws(data, smooth_radius=5, threshold=0.0001):
"""Detects and isolates contiguous regions in the input array"""
data = sp.ndimage.uniform_filter(data, smooth_radius)
thresh = data > threshold
filled = sp.ndimage.morphology.binary_fill_holes(thresh)
coded_paws, num_paws = sp.ndimage.label(filled)
data_slices = sp.ndimage.find_objects(coded_paws)
return data_slices
def paw_file(filename):
"""Returns a iterator that yields the time and data in each frame
The infile is an ascii file of timesteps formatted similar to this:
Frame 0 (0.00 ms)
0.0 0.0 0.0
0.0 0.0 0.0
Frame 1 (0.53 ms)
0.0 0.0 0.0
0.0 0.0 0.0
...
"""
with open(filename) as infile:
while True:
try:
time, data = read_frame(infile)
yield time, data
except StopIteration:
break
def read_frame(infile):
"""Reads a frame from the infile."""
frame_header = infile.next().strip().split()
time = float(frame_header[-2][1:])
data = []
while True:
line = infile.next().strip().split()
if line == []:
break
data.append(line)
return time, np.array(data, dtype=np.float)
if __name__ == '__main__':
animate('Overlapping paws.bin')
animate('Grouped up paws.bin')
animate('Normal measurement.bin')
Cập nhật: Đối với việc xác định con nào tiếp xúc với cảm biến vào thời điểm nào, giải pháp đơn giản nhất là chỉ thực hiện phân tích tương tự, nhưng sử dụng tất cả dữ liệu cùng một lúc. (tức là xếp dữ liệu đầu vào vào một mảng 3D và làm việc với nó, thay vì các khung thời gian riêng lẻ.) Vì các hàm ndimage của SciPy có nghĩa là hoạt động với các mảng n chiều, chúng tôi không phải sửa đổi hàm tìm chân gốc. ở tất cả.
def paw_regions(infile):
data, time = [], []
for t, frame in paw_file(infile):
time.append(t)
data.append(frame)
data = np.dstack(data)
time = np.asarray(time)
data_slices, coded_paws = find_paws(data, smooth_radius=4)
data_slices.sort(key=lambda dat_slice: dat_slice[2].start)
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
annotate_paw_prints(time, data, data_slices, ax=ax1)
ax2 = fig.add_subplot(2,1,2)
plot_paw_impacts(time, data_slices, ax=ax2)
fig.suptitle(infile)
def plot_paw_impacts(time, data_slices, ax=None):
if ax is None:
ax = plt.gca()
for i, dat_slice in enumerate(data_slices):
dx, dy, dt = dat_slice
paw = i%4 + 1
ax.barh(bottom=paw, width=time[dt].ptp(), height=0.2,
left=time[dt].min(), align='center', color='red')
ax.set_yticks(range(1, 5))
ax.set_yticklabels(['Paw 1', 'Paw 2', 'Paw 3', 'Paw 4'])
ax.set_xlabel('Time (ms) Since Beginning of Experiment')
ax.yaxis.grid(True)
ax.set_title('Periods of Paw Contact')
def annotate_paw_prints(time, data, data_slices, ax=None):
if ax is None:
ax = plt.gca()
ax.imshow(data.sum(axis=2).T)
x, y = [], []
for i, region in enumerate(data_slices):
dx, dy, dz = region
x0 = 0.5 * (dx.start + dx.stop)
y0 = 0.5 * (dy.start + dy.stop)
x.append(x0); y.append(y0)
ax.annotate('Paw %i' % (i%4 +1), (x0, y0),
color='red', ha='center', va='bottom')
ax.plot(x,y, '-wo')
ax.axis('image')
ax.set_title('Order of Steps')