programing

어떻게 하면 발의 감지를 개선할 수 있을까요?

newsource 2022. 9. 19. 23:43

어떻게 하면 발의 감지를 개선할 수 있을까요?

발끝을 찾는 것에 대한 이전 질문 이후, 나는 그것이 어떻게 지탱하는지 보기 위해 다른 치수를 로드하기 시작했다.불행히도, 저는 바로 앞의 단계 중 하나인 발을 인식하는 문제에 부딪혔습니다.

보시다시피, 개념 증명은 기본적으로 시간에 따라 각 센서의 최대 압력을 취했으며 각 행의 합계를 찾기 시작했습니다. 이 합계가 != 0.0을 찾을 때까지요.그런 다음 열에 대해 동일한 작업을 수행하고 0인 행이 3개 이상 다시 발견되면 즉시 이 작업을 수행합니다.일부 인덱스에 최소 및 최대 행 및 열 값을 저장합니다.

alt 텍스트

그림에서 볼 수 있듯이 대부분의 경우 이 기능은 매우 잘 작동합니다.단, 이 접근법에는 (매우 원시적인 것 이외에는) 많은 단점이 있습니다.

  • 인간은 '홀로우 발'을 가질 수 있는데, 이것은 발자국 안에 빈 줄이 몇 개 있다는 것을 의미한다.(큰) 개도 이런 일이 생길까 봐 적어도 2, 3열은 기다렸다가 발을 잘랐습니다.

    이로 인해 다른 연락처가 빈 행에 도달하기 전에 다른 열에서 작성된 경우 문제가 발생하여 영역이 확장됩니다.기둥을 비교해서 특정 값을 초과하면 다른 발이어야 한다는 걸 알 수 있을 것 같아요

  • 이 문제는 개가 매우 작거나 더 빠른 속도로 걸을 때 악화된다.앞발의 발가락은 여전히 접촉하고 있는 반면 뒷발의 발가락은 앞발과 같은 영역 내에서 접촉하기 시작합니다!

    간단한 스크립트에서는 이 두 개의 프레임을 분할할 수 없습니다.그 영역의 어느 프레임이 어느 paw에 속하는지 판별해야 하기 때문입니다.현재는 모든 프레임의 최대값만 조사하면 됩니다.

문제가 발생하기 시작하는 예를 다음에 제시하겠습니다.

alt 텍스트 alt 텍스트

그래서 나는 이제 발을 인식하고 분리하는 더 나은 방법을 찾고 있다(그 후에 어떤 발인지 결정하는 문제에 도달할 것이다!).

업데이트:

Joe의 (대박!) 답변을 구현하기 위해 계속 노력하고 있지만, 파일에서 실제 발 데이터를 추출하는 데 어려움을 겪고 있습니다.

alt 텍스트

coded_paws는 최대 압력 이미지에 적용되었을 때 모든 다양한 발을 보여 줍니다(위 참조).그러나 솔루션은 각 프레임을 검토하여 (중복되는 발을 분리하기 위해) 좌표 또는 높이/폭과 같은 4개의 직사각형 속성을 설정합니다.

이러한 속성을 측정 데이터에 적용할 수 있는 변수에 저장하는 방법을 찾을 수 없습니다.각 발가락에 대해 알아야 하므로 프레임의 위치와 이 프레임의 앞/뒤집기, 왼쪽/오른쪽 발을 연결해야 합니다.

그러면 직사각형 속성을 사용하여 각 발에 대해 이러한 값을 추출하려면 어떻게 해야 합니까?

공개 Dropbox 폴더( 1, 2, 3)에 질문 설정에 사용한 측정값이 있습니다.관심 있는 분들을 위해 블로그도 개설하여 최신 정보를 제공하고 있습니다:-)

인접 지역(세미)을 원하는 경우 Python에는 이미 간단한 구현이 있습니다.SciPyndimage.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 에게 알리다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~포의 수)의 연속 영역이며, 그 외의 모든 위치에 0이 있습니다).

  5. 을 격리합니다.data_slices = sp.ndimage.find_objects(coded_paws)의됩니다.slice 각 할 수 .[data[x] for x in data_slices]대신 이 슬라이스를 바탕으로 직사각형을 그리므로 작업이 조금 더 필요합니다.


아래 두 애니메이션은 "Overlapping Paws" 및 "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 어레이에 쌓고 개별 시간대가 아닌 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')

alt 텍스트


alt 텍스트


alt 텍스트

이미지 검출에 대한 전문가도 아니고 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가 바뀌면 오류에 대항하는 것으로 간주됩니다).

어느 프레임에서 회전하는지 알면 압력이 가장 높았던 프레임을 알 수 있고 두 발 중 어느 쪽이 가장 힘들지 알 수 있습니다.이론상으로는 발이 가장 세게 눌린 두 개의 프레임을 알 수 있으며 이러한 간격의 평균을 계산할 수 있습니다.

그 후엔 어떤 발인지 결정하는 문제에 착수할 거야!

이것은 이전과 같은 투어입니다, 각 발이 언제 가장 많은 압력을 가하는지 알면 결정을 내리는 데 도움이 됩니다.

언급URL : https://stackoverflow.com/questions/4087919/how-can-i-improve-my-paw-detection