我怎样才能提高我的爪子检测?
在我之前关于在每个爪子里find脚趾的问题之后,我开始加载其他的测量,看看它会如何保持。 不幸的是,我很快就遇到了一个前面提到的问题:认出爪子。
你看,我的概念certificate基本上花了每个传感器的最大压力随着时间的推移,并开始寻找每一行的总和,直到它find了!= 0.0。 然后它对列进行相同的操作,一旦发现多于2行的数据再次为零。 它将最小和最大的行和列值存储到某个索引。
从图中可以看出,在大多数情况下,这种方法效果很好。 然而,这种方法有很多缺点(除非是非常原始的):
-
人类可以有“空心的脚”,这意味着足迹本身有几个空行。 因为我担心这可能会发生在(大)狗,所以在切断爪子之前,我至less要等2,3或3个空行。
如果另一个联系人在达到多个空行之前在另一个联系人中创build联系人,则会产生问题,从而扩大该区域。 我想我可以比较列,看看他们是否超过一定的价值,他们必须是单独的爪子。
-
当狗非常小或步伐较快时,问题会变得更糟。 会发生什么事情呢,前爪的脚趾还在接触,而后爪的脚趾刚刚开始与前爪在同一区域内接触!
用我的简单的脚本,它将不能够分裂这两个,因为它将不得不确定该区域哪些帧属于哪个爪子,而目前我只需要看所有帧的最大值。
哪里开始出错的例子:
所以现在我正在寻找一个更好的方式来识别和分离爪子 (之后我会去决定它是哪个爪子的问题!)。
更新:
我一直在修补,以实现乔的(真棒!)答案,但我很难从我的文件中提取实际的爪子数据。
当应用于最大压力图像时,coded_paws显示了所有不同的爪子(参见上文)。 但是,解决scheme遍历每一帧(以分离重叠的爪子),并设置四个矩形属性,如坐标或高度/宽度。
我无法弄清楚如何把这些属性存储在一些可以应用于测量数据的variables中。 因为我需要知道每一个爪子,它的位置是在哪个框架中,并且将它连接到哪个爪子(前/后,左/右)。
那么如何使用Rectangles属性为每个爪子提取这些值呢?
我有我在我的公共Dropbox文件夹中的问题设置中使用的测量( 示例1 , 示例2 , 示例3 )。 对于任何感兴趣的人,我还设置了一个博客 ,让你保持最新:-)
如果你只是想要(半)连续的区域,Python已经有了一个简单的实现: SciPy的ndimage.morphology模块。 这是一个相当常见的图像形态学操作。
基本上,你有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
-
模糊input数据一点,以确保爪子有一个连续的足迹。 (使用更大的内核(
structure
kwarg到各种scipy.ndimage.morphology
函数)会更有效率,但由于某种原因,这种方法不能正常工作) -
阈值数组,以便您有压力超过某个阈值(即
thresh = data > value
)的位置的布尔数组 -
填充任何内部的洞,以便有更清洁的区域(
filled = sp.ndimage.morphology.binary_fill_holes(thresh)
) -
find单独的连续区域(
coded_paws, num_paws = sp.ndimage.label(filled)
)。 这将返回一个数组(每个区域是一个唯一的整数(1到最多的爪子数)的连续区域,其他地方都是零))。 -
使用
data_slices = sp.ndimage.find_objects(coded_paws)
隔离连续的区域。 这将返回slice
对象的元组列表,因此您可以使用[data[x] for x in data_slices]
每个爪子的数据区域。 相反,我们将根据这些切片绘制一个矩形,这需要稍微多一点的工作。
下面的两个animation显示你的“重叠的爪子”和“分组的爪子”的例子数据。 这种方法似乎是完美的。 (无论它值多less钱,它比我的机器上的GIF图像更顺畅,所以爪子检测algorithm相当快…)
这是一个完整的例子(现在有更详细的解释)。 这绝大多数是阅读input和制作animation。 实际的爪子检测只有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')
更新:至于在什么时候确定哪个爪子与传感器接触,最简单的解决scheme就是做同样的分析,但是一次使用所有的数据。 (即将input叠加到一个3D数组中,而不是单独的时间框架)。由于SciPy的ndimage函数是用来处理n维数组的,所以我们不必修改原来的寻找函数在所有。
# 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')
我不是图像检测专家,我不知道Python,但我会给它一个重击…
要检测单个爪子,首先应该select压力大于某个小阈值的一切,非常接近无压力。 每个像素/点以上都应该被标记。 然后,与所有“标记”像素相邻的每个像素被标记,并且这个过程被重复几次。 完全连接的群体将形成,所以你有不同的对象。 那么,每个“对象”都有最小值和最大值x和y值,所以边界框可以整齐地围绕它们。
伪代码:
(MARK) ALL PIXELS ABOVE (0.5)
(MARK) ALL PIXELS (ADJACENT) TO (MARK) PIXELS
REPEAT (STEP 2) (5) TIMES
SEPARATE EACH TOTALLY CONNECTED MASS INTO A SINGLE OBJECT
MARK THE EDGES OF EACH OBJECT, AND CUT APART TO FORM SLICES.
这应该关于做到这一点。
注:我说像素,但这可能是使用平均像素的区域。 优化是另一个问题…
听起来就像你需要分析每个像素的function(压力随时间变化),并确定函数转向的位置 (当它在另一个方向上改变> X时,它被认为是一个反转错误的转向)。
如果你知道它在哪个帧上转动,你就会知道压力最大的那个帧,你会知道两个爪子之间哪个是最难的。 从理论上讲,你会知道爪子最难按下的两个框架,可以计算出这些间隔的平均值。
之后我会去决定哪个爪子的问题!
这和以前一样,知道什么时候每个爪子适用最多的压力帮助你决定。