หากคุณต้องการพื้นที่กึ่งประชิดมีการใช้งานที่ง่ายใน Python: โมดูลndimage.morphologyของSciPy นี่คือการดำเนินการสัณฐานวิทยาของภาพที่ค่อนข้างธรรมดา
โดยทั่วไปคุณมี 5 ขั้นตอน:
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
เบลอข้อมูลอินพุตเล็กน้อยเพื่อให้แน่ใจว่าอุ้งเท้ามีรอยต่อเนื่อง (มันจะมีประสิทธิภาพมากกว่าเพียงแค่ใช้เคอร์เนลที่มีขนาดใหญ่กว่า (the structure
kwarg ไปยังscipy.ndimage.morphology
ฟังก์ชั่นต่าง ๆ) แต่มันไม่ทำงานอย่างถูกต้องด้วยเหตุผลบางอย่าง ... )
กำหนดค่าอาร์เรย์เพื่อให้คุณมีอาร์เรย์บูลีนของสถานที่ที่แรงดันมีค่าเกินกว่าค่าที่กำหนดไว้ (เช่นthresh = data > value
)
เติมช่องว่างภายในเพื่อให้คุณมีพื้นที่ที่สะอาดขึ้น ( filled = sp.ndimage.morphology.binary_fill_holes(thresh)
)
ค้นหาภูมิภาคที่ต่อเนื่องกัน ( coded_paws, num_paws = sp.ndimage.label(filled)
) สิ่งนี้จะส่งคืนอาร์เรย์ที่มีภูมิภาคที่มีรหัสกำกับไว้เป็นตัวเลข (แต่ละภูมิภาคเป็นพื้นที่ต่อเนื่องของเลขจำนวนเต็มเฉพาะ (1 ถึงจำนวนของอุ้งเท้า) โดยมีศูนย์อยู่ที่อื่น))
data_slices = sp.ndimage.find_objects(coded_paws)
แยกดินแดนที่อยู่ติดกันโดยใช้ ผลตอบแทนนี้รายการ tuples ของวัตถุเพื่อให้คุณสามารถได้รับพื้นที่ของข้อมูลสำหรับแต่ละตีนกับslice
[data[x] for x in data_slices]
แต่เราจะวาดสี่เหลี่ยมตามชิ้นส่วนเหล่านี้ซึ่งใช้งานได้มากกว่าเดิมเล็กน้อย
ภาพเคลื่อนไหวสองภาพด้านล่างแสดงข้อมูลตัวอย่าง "Overlapping Paws" และ "Paws ที่จัดกลุ่ม" ของคุณ วิธีนี้ดูเหมือนว่าจะทำงานได้อย่างสมบูรณ์ (และสำหรับสิ่งที่คุ้มค่ามันทำงานได้ราบรื่นกว่าภาพ GIF ด้านล่างในเครื่องของฉันดังนั้นอัลกอริทึมการตรวจจับอุ้งเท้าก็ค่อนข้างเร็ว ... )
นี่คือตัวอย่างเต็มรูปแบบ (ตอนนี้พร้อมคำอธิบายที่ละเอียดยิ่งขึ้น) ส่วนใหญ่นี้จะอ่านอินพุตและสร้างภาพเคลื่อนไหว การตรวจจับอุ้งเท้าที่แท้จริงคือรหัสเพียง 5 บรรทัด
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"""
# With matplotlib, it's much, much faster to just update the properties
# of a display object than it is to create a new one, so we'll just update
# the data and position of the same objects throughout this animation...
infile = paw_file(input_filename)
# Since we're making an animation with matplotlib, we need
# ion() instead of show()...
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
fig.suptitle(input_filename)
# Make an image based on the first frame that we'll update later
# (The first frame is never actually displayed)
im = ax.imshow(infile.next()[1])
# Make 4 rectangles that we can later move to the position of each paw
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')
# Process and display each frame
for time, frame in infile:
paw_slices = find_paws(frame)
# Hide any rectangles that might be visible
[rect.set_visible(False) for rect in rects]
# Set the position and size of a rectangle for each paw and display it
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)
# Update the image data and title of the plot
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"""
# Blur the input data a bit so the paws have a continous footprint
data = sp.ndimage.uniform_filter(data, smooth_radius)
# Threshold the blurred data (this needs to be a bit > 0 due to the blur)
thresh = data > threshold
# Fill any interior holes in the paws to get cleaner regions...
filled = sp.ndimage.morphology.binary_fill_holes(thresh)
# Label each contiguous paw
coded_paws, num_paws = sp.ndimage.label(filled)
# Isolate the extent of each paw
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')
อัปเดต:เท่าที่ระบุว่าอุ้งเท้าอันใดที่สัมผัสกับเซ็นเซอร์ในเวลาใดโซลูชั่นที่ง่ายที่สุดคือทำการวิเคราะห์แบบเดียวกัน แต่ใช้ข้อมูลทั้งหมดในครั้งเดียว (เช่นสแต็กอินพุตลงในอาร์เรย์ 3D และทำงานกับมันแทนกรอบเวลาของแต่ละบุคคล) เนื่องจากฟังก์ชั่น ndimage ของ SciPy นั้นมีความหมายที่จะทำงานกับอาร์เรย์ n-Dim เราไม่จำเป็นต้องแก้ไขฟังก์ชั่นการค้นหาอุ้งเท้าดั้งเดิม เลย
# This uses functions (and imports) in the previous code example!!
def paw_regions(infile):
# Read in and stack all data together into a 3D array
data, time = [], []
for t, frame in paw_file(infile):
time.append(t)
data.append(frame)
data = np.dstack(data)
time = np.asarray(time)
# Find and label the paw impacts
data_slices, coded_paws = find_paws(data, smooth_radius=4)
# Sort by time of initial paw impact... This way we can determine which
# paws are which relative to the first paw with a simple modulo 4.
# (Assuming a 4-legged dog, where all 4 paws contacted the sensor)
data_slices.sort(key=lambda dat_slice: dat_slice[2].start)
# Plot up a simple analysis
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()
# Group impacts by paw...
for i, dat_slice in enumerate(data_slices):
dx, dy, dt = dat_slice
paw = i%4 + 1
# Draw a bar over the time interval where each paw is in contact
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()
# Display all paw impacts (sum over time)
ax.imshow(data.sum(axis=2).T)
# Annotate each impact with which paw it is
# (Relative to the first paw to hit the sensor)
x, y = [], []
for i, region in enumerate(data_slices):
dx, dy, dz = region
# Get x,y center of slice...
x0 = 0.5 * (dx.start + dx.stop)
y0 = 0.5 * (dy.start + dy.stop)
x.append(x0); y.append(y0)
# Annotate the paw impacts
ax.annotate('Paw %i' % (i%4 +1), (x0, y0),
color='red', ha='center', va='bottom')
# Plot line connecting paw impacts
ax.plot(x,y, '-wo')
ax.axis('image')
ax.set_title('Order of Steps')