2010-11-03 26 views
184

Sau câu hỏi trước đây của tôi trên finding toes within each paw, tôi bắt đầu tải lên các phép đo khác để xem nó sẽ giữ vững như thế nào. Thật không may, tôi nhanh chóng chạy vào một vấn đề với một trong các bước trước: công nhận bàn chân.Làm cách nào để cải thiện khả năng phát hiện chân của tôi?

Bạn thấy đấy, bằng chứng khái niệm cơ bản của tôi đã áp lực tối đa của mỗi cảm biến theo thời gian và sẽ bắt đầu tìm kiếm tổng của mỗi hàng, cho đến khi nó tìm thấy trên đó! Sau đó, nó làm tương tự cho các cột và ngay sau khi nó tìm thấy nhiều hơn 2 hàng với đó là số không một lần nữa. Nó lưu trữ các giá trị hàng và cột tối thiểu và tối đa cho một số chỉ mục.

alt text

Như bạn thấy trong hình, điều này hoạt động khá tốt trong hầu hết các trường hợp. Tuy nhiên, có rất nhiều nhược điểm để phương pháp này (khác hơn là rất thô sơ):

  • Con người có thể có 'chân rỗng' có nghĩa là có một số hàng có sản phẩm nào trong dấu chân của chính nó. Vì tôi sợ điều này có thể xảy ra với những con chó (lớn), tôi đã chờ ít nhất 2 hoặc 3 hàng trống trước khi cắt chân.

    Điều này tạo ra sự cố nếu một liên hệ khác được tạo trong một cột khác trước khi nó tiếp cận một số hàng trống, do đó mở rộng khu vực. Tôi con số tôi có thể so sánh các cột và xem nếu họ vượt quá một giá trị nhất định, họ phải được bàn chân riêng biệt.

  • Sự cố trở nên tồi tệ hơn khi chú chó rất nhỏ hoặc đi với tốc độ cao hơn. Điều gì xảy ra là ngón chân của chân trước vẫn tiếp xúc, trong khi ngón chân chân sau chỉ bắt đầu tiếp xúc trong cùng khu vực với chân trước!

    Với kịch bản đơn giản của tôi, nó sẽ không thể tách hai, bởi vì nó sẽ phải xác định khung của khu vực đó thuộc về chân nào, trong khi hiện tại tôi sẽ chỉ phải xem xét các giá trị tối đa trên tất cả khung.

Ví dụ về nơi nó bắt đầu đi sai:

alt text alt text

Vì vậy, bây giờ tôi đang tìm kiếm một cách tốt hơn để nhận biết và tách bàn chân (sau đó tôi sẽ nhận được vấn đề quyết định mà chân nó là!).

Cập nhật:

Tôi đã mày mò để có được (tuyệt vời!) Câu trả lời của Joe thực hiện, nhưng tôi đang gặp khó khăn giải nén dữ liệu chân thực từ các tập tin của tôi.

alt text

Các coded_paws cho tôi thấy tất cả các bàn chân khác nhau, khi áp dụng cho các hình ảnh áp tối đa (xem ở trên). Tuy nhiên, giải pháp đi qua mỗi khung hình (để tách các chồng chéo) và đặt bốn thuộc tính Hình chữ nhật, chẳng hạn như toạ độ hoặc chiều cao/chiều rộng.

Tôi không thể tìm ra cách thực hiện các thuộc tính này và lưu trữ chúng trong một số biến mà tôi có thể áp dụng cho dữ liệu đo lường. Vì tôi cần phải biết cho mỗi chân, vị trí của nó là gì trong đó khung và cặp đôi này mà chân nó là (trước/sau, trái/phải).

Vậy làm cách nào tôi có thể sử dụng các thuộc tính Hình chữ nhật để trích xuất các giá trị này cho mỗi chân?

Tôi có các số đo tôi đã sử dụng trong thiết lập câu hỏi trong thư mục Dropbox công khai của tôi (example 1, example 2, example 3). For anyone interested I also set up a blog để giữ cho bạn cập nhật :-)

+0

Có vẻ như bạn sẽ phải quay lưng lại với các thuật toán dòng/cột một bạn đang hạn chế thông tin hữu ích. –

+12

Thật tuyệt vời! Phần mềm điều khiển mèo? – alxx

+0

Dữ liệu về chó thực sự là @alxx ;-) Nhưng vâng, nó sẽ được sử dụng để chẩn đoán chúng! –

Trả lời

344

Nếu bạn chỉ muốn (bán) các vùng lân cận, đã có một triển khai dễ dàng bằng Python: SciPy 's mô-đun ndimage.morphology. Đây là một hoạt động khá phổ biến image morphology.


Về cơ bản, bạn có 5 bước sau:

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. Blur dữ liệu đầu vào một chút để đảm bảo bàn chân có một dấu chân liên tục. (Sẽ hiệu quả hơn khi chỉ sử dụng một hạt nhân lớn hơn (số structure kwarg cho các hàm scipy.ndimage.morphology khác nhau) nhưng điều này không hoạt động đúng cách vì một số lý do ...)

  2. Ngưỡng mảng sao cho bạn có mảng boolean nơi cất áp lực là trên một số giá trị ngưỡng (tức thresh = data > value)

  3. Điền bất kỳ lỗ nội bộ, nên bạn chỉ còn khu vực sạch hơn (filled = sp.ndimage.morphology.binary_fill_holes(thresh))

  4. Tìm các khu vực 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 theo số (mỗi vùng là một vùng liền kề của một số nguyên duy nhất (1 đến số lượng bàn chân) với các số không ở mọi nơi khác)).

  5. Cô lập các vùng lân cận bằng cách sử dụng data_slices = sp.ndimage.find_objects(coded_paws). Điều này trả về một danh sách các bộ gồm các đối tượng slice, do đó bạn có thể lấy vùng dữ liệu cho mỗi chân bằng [data[x] for x in data_slices]. Thay vào đó, chúng tôi sẽ vẽ một hình chữ nhật dựa trên những lát cắt này, điều này sẽ tốn nhiều công sức hơn một chút.


Hai hình ảnh động dưới đây hiển thị "chồng chéo Paws" và "nhóm Paws" dữ liệu ví dụ của bạn. Phương pháp này dường như hoạt động hoàn hảo. (Và đối với bất cứ điều gì nó có giá trị, này chạy nhiều thuận lợi hơn so với hình ảnh GIF bên dưới về máy của tôi, vì vậy các thuật toán phát hiện chân khá nhanh ...)

Overlapping Paws Grouped Paws


Dưới đây là một đầy đủ ví dụ (bây giờ với nhiều giải thích chi tiết hơn). Phần lớn điều này là đọc đầu vào và tạo ra một hình ảnh động. Việc phát hiện chân thực 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""" 
    # 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') 

Cập nhật: Theo như xác định những chân tiếp xúc với bộ cảm biến vào những gì thời gian, giải pháp đơn giản nhất là chỉ cần làm các phân tích tương tự, nhưng sử dụng tất cả các dữ liệu cùng một lúc. (tức là ngăn xếp đầ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ẻ.) Bởi vì các hàm của SciPy có nghĩa là làm việc với các mảng n-chiều, chúng ta không phải sửa đổi hàm tìm kiếm gốc ở tất cả.

# 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

Tôi thậm chí không thể bắt đầu giải thích mức độ tuyệt vời mà bạn trả lời! –

+0

@Ivo - Rất vui được trợ giúp! Đó là một tập dữ liệu thú vị và một vấn đề gọn gàng! –

+0

@Joe: Câu trả lời hay! Làm thế nào bạn tạo ra các gif? Tôi có thể 'plt.savefig (...)' để tạo một tập hợp các png, nhưng 'chuyển đổi * .png output.gif' của imagemagick đưa máy của tôi đến đầu gối của nó ... – unutbu

3

Tôi không phải chuyên gia trong việc phát hiện hình ảnh, và tôi không biết Python, nhưng tôi sẽ cho nó một Whack ...

Phát hiện bàn chân cá nhân , trước tiên bạn chỉ nên chọn mọi thứ với áp lực lớn hơn một số ngưỡng nhỏ, rất gần với không có áp lực nào cả. Mỗi điểm ảnh/điểm phía trên điểm ảnh này phải được "đánh dấu". Sau đó, mọi pixel liền kề với tất cả các pixel "được đánh dấu" sẽ được đánh dấu và quá trình này được lặp lại vài lần. Khối lượng được kết nối hoàn toàn sẽ được hình thành, vì vậy bạn có các đối tượng riêng biệt. Sau đó, mỗi "đối tượng" có giá trị x và y tối thiểu và tối đa, vì vậy các hộp giới hạn có thể được đóng gói gọn gàng xung quanh chúng.

Mã giả:

(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.

Điều đó nên làm.

0

Lưu ý: Tôi nói pixel, nhưng đây có thể là các khu vực sử dụng điểm trung bình của pixel. Tối ưu hóa là một vấn đề khác ...

Có vẻ như bạn cần phân tích hàm (áp lực theo thời gian) cho mỗi pixel và xác định where the function turns (khi thay đổi> X theo hướng ngược lại, nó được coi là lỗi truy cập).

Nếu bạn biết những gì khung quay, bạn sẽ biết khung nơi áp lực là khó khăn nhất và bạn sẽ biết nơi mà nó là khó khăn nhất giữa hai bàn chân. Về lý thuyết, sau đó bạn sẽ biết hai khung hình nơi bàn chân nhấn mạnh nhất và có thể tính toán trung bình của những khoảng thời gian đó.

sau đó tôi sẽ gặp sự cố khi quyết định xem nó là cái nào!

Đây là chuyến đi tương tự như trước đây, khi biết khi nào mỗi chân áp dụng nhiều áp lực nhất sẽ giúp bạn quyết định.

Các vấn đề liên quan