[python] 내 발을 분류하는 방법?

에서 내 앞의 질문 나는 훌륭한 답변을 얻었다 앞발이 압력 판을 칠 경우 나 감지 도움이,하지만 지금은 그에 상응하는 발에이 결과를 링크 사투를 벌인거야 :

대체 텍스트

발에 수동으로 주석을 달았습니다 (RF = 오른쪽 앞, RH = 오른쪽 뒷다리, LF = 왼쪽 앞, LH = 왼쪽 뒷다리).

보시다시피 분명히 반복되는 패턴이 있으며 거의 ​​모든 측정에서 다시 나타납니다. 다음은 수동으로 주석이 추가 된 6 개의 시행 프레젠테이션에 대한 링크입니다.

내 초기 생각은 휴리스틱을 사용하여 다음과 같은 정렬을 수행하는 것이 었습니다.

  • 앞발과 뒷발 사이의 무게 베어링 비율은 ~ 60-40 %입니다.
  • 뒷발은 일반적으로 표면이 더 작습니다.
  • 발은 (종종) 공간적으로 왼쪽과 오른쪽으로 나뉩니다.

그러나 나는 생각하지 못했던 변형을 만나 자마자 실패 할 것이기 때문에 내 휴리스틱에 대해 약간 회의적입니다. 그들은 또한 그들 자신의 규칙을 가지고있을 것 같은 절름발이 개들의 측정에 대처할 수 없을 것입니다.

또한 Joe가 제안한 주석은 때때로 엉망이되어 발이 실제로 어떻게 보이는지 고려하지 않습니다.

발 내 피크 감지에 대한 질문에 대한 답변을 기반으로 을 분류하는 더 고급 솔루션이 있기를 바랍니다. 특히 지문처럼 각 발마다 압력 분포와 진행이 다르기 때문이다. 나는 발을 발생 순서대로 분류하는 것이 아니라 이것을 사용하여 내 발을 묶을 수있는 방법이 있기를 바랍니다.

대체 텍스트

따라서 해당 발로 결과를 정렬하는 더 좋은 방법을 찾고 있습니다.

도전에 직면 한 모든 사람을 위해 각 발의 압력 데이터 (측정에 의해 번들로 제공됨) 가 포함 된 모든 슬라이스 배열해당 위치 (플레이트상의 위치 및 시간) 를 설명하는 슬라이스가 포함 된 사전절임했습니다 .

명확하게 말하면 : walk_sliced_data는 측정 이름 인 [ ‘ser_3’, ‘ser_2’, ‘sel_1’, ‘sel_2’, ‘ser_1’, ‘sel_3’]을 포함하는 사전입니다. 각 측정에는 추출 된 영향을 나타내는 또 다른 사전 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ( ‘sel_1’의 예)이 포함됩니다.

또한 발이 부분적으로 측정되는 위치 (공간 또는 시간)와 같은 ‘거짓’영향은 무시할 수 있습니다. 패턴을 인식하는 데 도움이 될 수 있기 때문에 유용하지만 분석되지는 않습니다.

그리고 관심있는 사람을 위해 프로젝트와 관련된 모든 업데이트를 블로그에 보관하고 있습니다!



답변

좋구나! 나는 마침내 일관되게 작동하는 것을 얻었습니다! 이 문제로 며칠 동안 저를 끌어 당겼습니다 … 재미있는 것들! 이 답변의 길이에 대해 죄송하지만 몇 가지에 대해 조금 더 자세히 설명해야합니다 … (스팸이 아닌 가장 긴 스택 오버플로 답변에 대한 기록을 세울 수는 있지만!)

참고로 Ivo 원래 질문 에서 링크를 제공 한 전체 데이터 세트를 사용하고 있습니다. 일련의 rar 파일 (개당 하나씩)은 각각 ascii 배열로 저장된 여러 다른 실험 실행을 포함합니다. 독립 실행 형 코드 예제를이 질문에 복사하여 붙여 넣는 대신 완전한 독립 실행 형 코드 가있는 bitbucket mercurial 저장소 가 있습니다. 다음으로 복제 할 수 있습니다.

hg clone https://joferkington@bitbucket.org/joferkington/paw-analysis


개요

질문에서 언급했듯이 본질적으로 문제에 접근하는 방법에는 두 가지가 있습니다. 실제로 두 가지를 서로 다른 방식으로 사용할 것입니다.

  1. 발 충격의 (시간적 및 공간적) 순서를 사용하여 어느 발인지 결정합니다.
  2. 순전히 모양을 기반으로 “발자국”을 식별하십시오.

기본적으로 첫 번째 방법은 강아지의 발이 위의 Ivo의 질문에 표시된 사다리꼴 모양의 패턴을 따르지만 발이 그 패턴을 따르지 않을 때마다 실패합니다. 작동하지 않을 때 프로그래밍 방식으로 감지하는 것은 매우 쉽습니다.

따라서 우리는 훈련 데이터 세트 (~ 30 개의 다른 개로부터 ~ 2000 개의 발 영향)를 구축하기 위해 작업 한 측정 값을 사용하여 어느 발이 어느 것인지 인식하고 문제는 감독 된 분류 (몇 가지 추가 주름 포함)로 축소됩니다. .. 이미지 인식은 “일반적인”감독 분류 문제보다 약간 더 어렵습니다).


패턴 분석

첫 번째 방법에 대해 자세히 설명하기 위해 개가 정상적으로 걷고있을 때 (달리지 않을 수 있습니다!) (이 개 중 일부는 그렇지 않을 수 있음) 앞발이 앞쪽 왼쪽, 뒷쪽 오른쪽, 앞쪽 오른쪽, 뒷쪽 왼쪽 순서로 충돌 할 것으로 예상합니다. , 전면 왼쪽 등. 패턴은 전면 왼쪽 또는 전면 오른쪽 발로 시작할 수 있습니다.

이것이 항상 그런 경우라면 초기 접촉 시간별로 영향을 분류하고 모듈로 4를 사용하여 발별로 그룹화 할 수 있습니다.

정상적인 영향 순서

그러나 모든 것이 “정상”인 경우에도 작동하지 않습니다. 이것은 사다리꼴 모양의 패턴 때문입니다. 뒷발은 이전 앞발 뒤에 공간적으로 떨어집니다.

따라서 초기 앞발 충돌 후 뒷발 충돌은 종종 센서 플레이트에서 떨어지며 기록되지 않습니다. 마찬가지로, 마지막 발 충격은 센서 플레이트에서 발생하기 전의 발 충격이 기록되지 않았기 때문에 시퀀스에서 다음 발이 아닌 경우가 많습니다.

놓친 뒷발

그럼에도 불구하고 우리는 발 충격 패턴의 모양을 사용하여 이것이 언제 발생했는지, 그리고 우리가 왼쪽 또는 오른쪽 앞발로 시작했는지 여부를 결정할 수 있습니다. (실제로 여기서 마지막 영향에 대한 문제를 무시하고 있습니다.하지만 추가하는 것은 그리 어렵지 않습니다.)

def group_paws(data_slices, time):
    # Sort slices by initial contact time
    data_slices.sort(key=lambda s: s[-1].start)

    # Get the centroid for each paw impact...
    paw_coords = []
    for x,y,z in data_slices:
        paw_coords.append([(item.stop + item.start) / 2.0 for item in (x,y)])
    paw_coords = np.array(paw_coords)

    # Make a vector between each sucessive impact...
    dx, dy = np.diff(paw_coords, axis=0).T

    #-- Group paws -------------------------------------------
    paw_code = {0:'LF', 1:'RH', 2:'RF', 3:'LH'}
    paw_number = np.arange(len(paw_coords))

    # Did we miss the hind paw impact after the first 
    # front paw impact? If so, first dx will be positive...
    if dx[0] > 0:
        paw_number[1:] += 1

    # Are we starting with the left or right front paw...
    # We assume we're starting with the left, and check dy[0].
    # If dy[0] > 0 (i.e. the next paw impacts to the left), then
    # it's actually the right front paw, instead of the left.
    if dy[0] > 0: # Right front paw impact...
        paw_number += 2

    # Now we can determine the paw with a simple modulo 4..
    paw_codes = paw_number % 4
    paw_labels = [paw_code[code] for code in paw_codes]

    return paw_labels

이 모든 것에도 불구하고 자주 올바르게 작동하지 않습니다. 전체 데이터 세트의 많은 개가 달리는 것처럼 보이며 발의 영향은 개가 걷고있을 때와 동일한 시간적 순서를 따르지 않습니다. (아니면 개에게 심각한 엉덩이 문제가있을 수도 있습니다 …)

비정상적인 영향 시퀀스

다행히도 우리는 발 충격이 예상되는 공간 패턴을 따르는 지 여부를 프로그래밍 방식으로 감지 할 수 있습니다.

def paw_pattern_problems(paw_labels, dx, dy):
    """Check whether or not the label sequence "paw_labels" conforms to our
    expected spatial pattern of paw impacts. "paw_labels" should be a sequence
    of the strings: "LH", "RH", "LF", "RF" corresponding to the different paws"""
    # Check for problems... (This could be written a _lot_ more cleanly...)
    problems = False
    last = paw_labels[0]
    for paw, dy, dx in zip(paw_labels[1:], dy, dx):
        # Going from a left paw to a right, dy should be negative
        if last.startswith('L') and paw.startswith('R') and (dy > 0):
            problems = True
            break
        # Going from a right paw to a left, dy should be positive
        if last.startswith('R') and paw.startswith('L') and (dy < 0):
            problems = True
            break
        # Going from a front paw to a hind paw, dx should be negative
        if last.endswith('F') and paw.endswith('H') and (dx > 0):
            problems = True
            break
        # Going from a hind paw to a front paw, dx should be positive
        if last.endswith('H') and paw.endswith('F') and (dx < 0):
            problems = True
            break
        last = paw
    return problems

따라서 단순 공간 분류가 항상 작동하는 것은 아니지만 합리적인 확신을 가지고 작동하는시기를 결정할 수 있습니다.

훈련 데이터 세트

올바르게 작동 한 패턴 기반 분류에서 올바르게 분류 된 발의 매우 큰 훈련 데이터 세트를 구축 할 수 있습니다 (32 개의 다른 개에서 ~ 2400 개의 발 충격!).

이제 “평균적인”앞 왼쪽 등의 앞발이 어떻게 생겼는지 살펴볼 수 있습니다.

이를 위해서는 모든 개에 대해 동일한 차원 인 일종의 “발 메트릭스”가 필요합니다. (전체 데이터 세트에는 매우 크고 작은 개가 모두 있습니다!) 아일랜드 엘크 하운드의 발 프린트는 토이 푸들의 발 프린트보다 훨씬 넓고 “무겁습니다”. a) 동일한 수의 픽셀을 가지며 b) 압력 값이 표준화되도록 각 발 인쇄의 크기를 조정해야합니다. 이를 위해 각 발 프린트를 20×20 그리드로 리샘플링하고 발 충격에 대한 최대, 최소 및 평균 압력 값을 기반으로 압력 값을 다시 조정했습니다.

def paw_image(paw):
    from scipy.ndimage import map_coordinates
    ny, nx = paw.shape

    # Trim off any "blank" edges around the paw...
    mask = paw > 0.01 * paw.max()
    y, x = np.mgrid[:ny, :nx]
    ymin, ymax = y[mask].min(), y[mask].max()
    xmin, xmax = x[mask].min(), x[mask].max()

    # Make a 20x20 grid to resample the paw pressure values onto
    numx, numy = 20, 20
    xi = np.linspace(xmin, xmax, numx)
    yi = np.linspace(ymin, ymax, numy)
    xi, yi = np.meshgrid(xi, yi)

    # Resample the values onto the 20x20 grid
    coords = np.vstack([yi.flatten(), xi.flatten()])
    zi = map_coordinates(paw, coords)
    zi = zi.reshape((numy, numx))

    # Rescale the pressure values
    zi -= zi.min()
    zi /= zi.max()
    zi -= zi.mean() #<- Helps distinguish front from hind paws...
    return zi

이 모든 후에, 우리는 평균적인 왼쪽 앞, 뒤 오른쪽 등의 발이 어떻게 생겼는지 마침내 살펴볼 수 있습니다. 이것은 크기가 크게 다른 30 마리 이상의 개에 대해 평균을 내고 있으며 일관된 결과를 얻는 것 같습니다!

평균 발

그러나 이들에 대한 분석을 수행하기 전에 평균 (모든 개의 모든 다리에 대한 평균 발)을 빼야합니다.

평균 발

이제 우리는 평균과의 차이를 분석 할 수 있습니다.

차동 발

이미지 기반 발 인식

좋아 .. 드디어 발을 일치시킬 수있는 패턴 세트가 생겼습니다. 각 발은 paw_image이 4 개의 400 차원 벡터와 비교할 수 있는 400 차원 벡터 ( 함수에 의해 반환 됨 )로 취급 될 수 있습니다.

불행히도 “정상적인”감독 분류 알고리즘을 사용하는 경우 (즉, 간단한 거리를 사용하여 4 가지 패턴 중 어떤 패턴이 특정 발바닥에 가장 가까운지를 찾는 것) 일관되게 작동하지 않습니다. 사실, 그것은 훈련 데이터 세트에서 임의의 기회보다 훨씬 더 나은 것은 아닙니다.

이것은 이미지 인식의 일반적인 문제입니다. 입력 데이터의 높은 차원 성과 이미지의 다소 “모호한”특성 (즉, 인접 픽셀의 공분산이 높음)으로 인해 템플릿 이미지에서 이미지의 차이를 보는 것만으로는 모양의 유사성.

Eigenpaws

이 문제를 해결하기 위해 우리는 “고유 발”세트 (얼굴 인식의 “고유 얼굴”과 마찬가지로)를 만들고 각 발 인쇄를 이러한 고유 발의 조합으로 설명해야합니다. 이것은 주성분 분석과 동일하며 기본적으로 데이터의 차원을 줄이는 방법을 제공하므로 거리가 모양의 좋은 척도가됩니다.

우리는 차원 (2400 대 400)보다 더 많은 훈련 이미지를 가지고 있기 때문에 속도를 위해 “멋진”선형 대수를 할 필요가 없습니다. 훈련 데이터 세트의 공분산 행렬로 직접 작업 할 수 있습니다.

def make_eigenpaws(paw_data):
    """Creates a set of eigenpaws based on paw_data.
    paw_data is a numdata by numdimensions matrix of all of the observations."""
    average_paw = paw_data.mean(axis=0)
    paw_data -= average_paw

    # Determine the eigenvectors of the covariance matrix of the data
    cov = np.cov(paw_data.T)
    eigvals, eigvecs = np.linalg.eig(cov)

    # Sort the eigenvectors by ascending eigenvalue (largest is last)
    eig_idx = np.argsort(eigvals)
    sorted_eigvecs = eigvecs[:,eig_idx]
    sorted_eigvals = eigvals[:,eig_idx]

    # Now choose a cutoff number of eigenvectors to use 
    # (50 seems to work well, but it's arbirtrary...
    num_basis_vecs = 50
    basis_vecs = sorted_eigvecs[:,-num_basis_vecs:]

    return basis_vecs

이것이 basis_vecs“고 유발”입니다.

Eigenpaws

이를 사용하기 위해 기본 벡터로 각 발 이미지 (20×20 이미지가 아닌 400 차원 벡터)를 도트 (예 : 행렬 곱셈)하면됩니다. 이것은 이미지를 분류하는 데 사용할 수있는 50 차원 벡터 (기본 벡터 당 하나의 요소)를 제공합니다. 20×20 이미지를 각 “템플릿”발의 20×20 이미지와 비교하는 대신 50 차원 변환 이미지를 각 50 차원 변환 템플릿 발과 비교합니다. 이것은 각 발가락이 정확히 어떻게 위치하는지 등의 작은 변화에 훨씬 덜 민감하며 기본적으로 문제의 차원을 관련 차원으로 줄입니다.

Eigenpaw 기반 Paw 분류

이제 각 다리에 대해 50 차원 벡터와 “템플릿”벡터 사이의 거리를 사용하여 어떤 발이 어느 것인지 분류 할 수 있습니다.

codebook = np.load('codebook.npy') # Template vectors for each paw
average_paw = np.load('average_paw.npy')
basis_stds = np.load('basis_stds.npy') # Needed to "whiten" the dataset...
basis_vecs = np.load('basis_vecs.npy')
paw_code = {0:'LF', 1:'RH', 2:'RF', 3:'LH'}
def classify(paw):
    paw = paw.flatten()
    paw -= average_paw
    scores = paw.dot(basis_vecs) / basis_stds
    diff = codebook - scores
    diff *= diff
    diff = np.sqrt(diff.sum(axis=1))
    return paw_code[diff.argmin()]

결과는 다음과 같습니다.
대체 텍스트
대체 텍스트
대체 텍스트

남은 문제

여전히 몇 가지 문제가 있습니다. 특히 개가 너무 작아서 명확한 발자국을 만들 수없는 경우 … (발가락이 센서의 해상도에서 더 명확하게 분리되므로 큰 개에서 가장 잘 작동합니다.) 또한이 방법으로 부분 발자국이 인식되지 않습니다. 시스템은 사다리꼴 패턴 기반 시스템을 사용할 수 있습니다.

그러나 eigenpaw 분석은 본질적으로 거리 측정법을 사용하기 때문에 발을 양방향으로 분류 할 수 있으며 “코드북”에서 eigenpaw 분석의 최소 거리가 임계 값을 초과 할 때 사다리꼴 패턴 기반 시스템으로 돌아갈 수 있습니다. 그래도 아직 구현하지 않았습니다.

휴 … 깁니다! 내 모자는 그런 재미있는 질문에 대해 Ivo에게 떨어져 있습니다!


답변

순전히 기간을 기반으로 한 정보를 사용하면 모델링 운동학의 기술을 적용 할 수 있다고 생각합니다. 즉 역 운동학 . 방향, 길이, 지속 시간 및 총 무게와 결합하여 일정 수준의 주기성을 제공합니다. 이것이 “발 분류”문제를 해결하는 첫 번째 단계가되기를 바랍니다.

이 모든 데이터를 사용하여 경계가있는 다각형 (또는 튜플) 목록을 만들 수 있습니다.이 목록을 사용하여 단계 크기별로 정렬 한 다음 paw-ness [index]별로 정렬 할 수 있습니다.


답변

테스트를 실행하는 기술자가 첫 번째 발 (또는 처음 두 발)을 수동으로 입력하도록 할 수 있습니까? 프로세스는 다음과 같습니다.

  • 기술자에게 단계 이미지의 순서를 보여주고 첫 발에 주석을 달도록 요구합니다.
  • 첫 번째 발을 기준으로 다른 발에 라벨을 붙이고 기술자가 수정하거나 테스트를 다시 실행할 수 있도록합니다. 이것은 절름발이 또는 3 다리 개를 허용합니다.

답변