2010-11-03 47 views
184

在我上一個問題finding toes within each paw之後,我開始加載其他測量值,看看它會如何保持。不幸的是,我很快遇到了上述步驟之一的問題:識別爪子。如何提高我的爪子檢測?

你看,我的概念驗證基本上花了每個傳感器的最大壓力隨着時間的推移,並開始尋找每一行的總和,直到它找到!= 0.0。然後它對列執行相同的操作,只要它發現多於2行的數據再次爲零。它將最小和最大行數和列值存儲到某個索引。

alt text

正如你在圖中看到,這工作得很好在大多數情況下。然而,也有很多缺點,以這種方式(比是非常原始的除外):

  • 人類可以有「空心腳」,這意味着有足跡本身內的幾個空行。因爲我擔心這可能會發生在(大)狗身上,所以我在切斷爪子之前等待至少2或3個空行。

    如果另一個聯繫人在達到多個空行之前在另一列中創建聯繫人,則會產生問題,從而擴大該區域。我想我可以比較欄目,看看它們是否超過了一定的價值,它們必須是單獨的爪子。

  • 當狗很小或步伐較快時,問題會變得更嚴重。會發生什麼情況是前爪的腳趾仍在接觸,而後爪的腳趾剛開始與前爪在同一區域內接觸!

    使用我的簡單腳本,它將無法分割這兩個,因爲它必須確定哪個區域的哪些幀屬於哪個爪子,而目前我只需要查看所有的最大值幀。腳麻它開始的地方

例子:

alt text alt text

所以現在我正在尋找識別和分離爪更好的辦法(之後我會去解決它的問題吧!)。

更新:

我一直在擺弄得到喬(真棒!)答案實現的,但我有從我的文件中提取的實際爪子數據的困難。

alt text

的coded_pa​​ws顯示我所有不同的爪子,當施加到最大壓力圖像(見上文)。但是,解決方案遍歷每個框架(以分離重疊的爪子)並設置四個Rectangle屬性,例如座標或高度/寬度。

我無法弄清楚如何獲取這些屬性並將它們存儲在某些我可以應用於測量數據的變量中。因爲我需要知道每個爪子,它的位置是在哪個框架中,並將它連接到哪個爪子(前/後,左/右)。

那麼如何使用矩形屬性爲每個爪子提取這些值?

我在我的公共Dropbox文件夾(example 1example 2example 3)的問題設置中使用了測量。 For anyone interested I also set up a blog讓您及時瞭解最新:-)

+0

好像你將不得不把從行/列算法遠的你限制有用的信息。 – 2010-11-03 22:21:48

+12

哇!貓控制軟件? – alxx 2010-11-10 08:34:34

+0

它實際上是狗的數據@alxx ;-)但是,它會被用來診斷它們! – 2010-11-10 08:37:04

回答

344

如果你只想要(半)連續的區域,Python中已經有一個簡單的實現:SciPyndimage.morphology模塊。這是相當常見的image 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 
  1. 模糊輸入的數據位,以確保爪子有一個連續的足跡。所以,你有一個(這將是更有效地只使用一個更大的內核(該structure kwarg的各種scipy.ndimage.morphology功能),但是,這是不是很出於某種原因正常工作......)

  2. 閾值數組的地方布爾數組,其中壓力超過某一閾值(即thresh = data > value

  3. 填寫任何內部孔,這樣就具有清潔器區域(filled = sp.ndimage.morphology.binary_fill_holes(thresh)

  4. 查找單獨連續的區域(coded_paws, num_paws = sp.ndimage.label(filled))。這將返回一個數組,其中的區域是由數字編碼的(每個區域都是一個唯一整數的鄰接區域(1到最多爪子的數量),其他地方都是零))。

  5. 使用data_slices = sp.ndimage.find_objects(coded_paws)分離連續區域。這會返回slice對象的元組列表,因此您可以使用[data[x] for x in data_slices]來獲取每個爪子的數據區域。相反,我們將根據這些切片繪製一個矩形,這需要稍微多些工作。


下面的兩個動畫顯示您的「重疊爪」和「分組爪」示例數據。這種方法似乎是完美的。 (而不管它的價值,這種運行更加平穩比下面的GIF圖像在我的機器上,因此爪子檢測算法是相當快的...)

Overlapping Paws Grouped Paws


這裏是一個完整的例子(現在有更詳細的解釋)。絕大多數是讀取輸入並製作動畫。實際的爪子檢測只有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維數組一起工作,因此我們不必修改原始爪查找函數在所有。

# 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') 

alt text


alt text


alt text

+79

我甚至無法解釋你真棒回答是多麼的棒! – 2010-11-04 16:03:11

+0

@Ivo - 很高興幫助!這是一個很酷的數據集,也是一個很好的問題! – 2010-11-04 16:17:09

+0

@Joe:很好的回答!你是如何製作GIF的?我可以'plt.savefig(...)'創建一個png集合,但是imagemagick的'convert * .png output.gif'會將我的機器帶到膝蓋上...... – unutbu 2010-11-04 20:15:01

3

我在圖像檢測方面的專家,我不知道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.

應該這樣做。

0

注:我說像素,但這可能是使用平均像素的區域。優化是另一個問題...

聽起來像你需要分析每個像素的函數(壓力隨時間變化),並確定where the function turns(當它在另一個方向上改變> X時,它被認爲是對抗錯誤的轉向)。

如果你知道它在哪個幀上轉動,你就會知道壓力最大的幀,並且你會知道兩個爪子之間的最小距離。理論上講,你會知道爪子最難按下的兩個框架,並且可以計算出這些間隔的平均值。

之後我會去決定它是哪個爪子的問題!

這是與以前相同的行程,瞭解每個爪子適用的最大壓力可以幫助您決定。