[英]How can I improve my paw detection?
在我之前關於在每個爪子中發現腳趾的問題之后,我開始加載其他測量值以查看它將如何保持。 不幸的是,我很快就遇到了上述步驟之一的問題:識別爪子。
你看,我的概念證明基本上是隨着時間推移每個傳感器的最大壓力,並開始尋找每一行的總和,直到它找到!= 0.0。 然后它對列進行相同的操作,一旦找到超過2行,再次為零。 它將最小和最大行和列值存儲到某個索引。
正如您在圖中所看到的,這在大多數情況下都能很好地工作。 但是,這種方法有許多缺點(除了非常原始):
人類可以擁有“空心腳”,這意味着足跡內部有幾排空行。 因為我擔心這種情況也會發生在(大)狗身上,所以在切斷爪子之前,我等待至少2或3個空行。
如果在到達多個空行之前在另一列中創建另一個聯系,則會產生問題,從而擴展該區域。 我想我可以比較列,看看它們是否超過某個值,它們必須是單獨的爪子。
當狗很小或走得更快時,問題會變得更糟。 發生的事情是,前爪的腳趾仍在接觸,而后爪的腳趾剛剛開始與前爪在同一區域內接觸!
使用我的簡單腳本,它將無法拆分這兩個,因為它必須確定該區域的哪些幀屬於哪個爪子,而目前我只需要查看所有幀的最大值。
它開始出錯的例子:
所以現在我正在尋找一種更好的識別和分離爪子的方法 (之后我將解決決定它是哪個爪子的問題!)。
更新:
我一直在修補Joe的(真棒!)答案,但是我很難從我的文件中提取實際的爪子數據。
當應用於最大壓力圖像時,coded_paws顯示了所有不同的爪子(見上文)。 但是,解決方案遍歷每個幀(以分隔重疊的爪子)並設置四個Rectangle屬性,例如坐標或高度/寬度。
我無法弄清楚如何獲取這些屬性並將它們存儲在一些我可以應用於測量數據的變量中。 因為我需要知道每個爪子,它在哪個框架中的位置是什么,並將它連接到哪個爪子(前/后,左/右)。
那么如何使用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
稍微模糊輸入數據以確保爪子具有連續的足跡。 (使用更大的內核( 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)
隔離連續區域。 這將返回slice
對象元組的列表,因此您可以使用[data[x] for x in data_slices]
每個爪子的數據區域。 相反,我們將基於這些切片繪制一個矩形,這需要稍微多一些工作。
下面的兩個動畫顯示了您的“重疊爪子”和“Grouped 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數組,並使用它,而不是單獨的時間幀。)因為SciPy的ndimage函數用於處理n維數組,所以我們不必修改原始的paw-finding函數一點都不
# 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,但我會給它一個打擊......
要檢測單個爪子,首先應選擇壓力大於某個小閾值的所有物體,非常接近無壓力。 應該“標記”高於此值的每個像素/點。 然后,與所有“標記”像素相鄰的每個像素被標記,並且該過程重復幾次。 將形成完全連接的質量,因此您有不同的對象。 然后,每個“對象”具有最小和最大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.
應該這樣做。
注意:我說像素,但這可能是使用平均像素的區域。 優化是另一個問題......
聽起來你需要分析每個像素的一個函數(壓力隨時間變化)並確定函數轉向的位置 (當它在另一個方向上改變> X時,它被視為轉向反擊錯誤)。
如果你知道它轉動的是什么樣的框架,你就會知道壓力最大的框架,你會知道兩只爪子之間的硬度最小。 從理論上講,你可以知道爪子壓得最厲害的兩個框架,並可以計算出這些間隔的平均值。
之后我會遇到決定它是哪個爪子的問題!
這是與以前相同的旅行,知道每個爪子施加最大壓力有助於您決定。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.