내 발을 어떻게 가려요?
이전 질문에서 저는 발이 압력판에 부딪힌 위치를 감지하는 데 도움이 되는 훌륭한 답변을 얻었지만, 지금은 이러한 결과를 해당 발과 연결하는 데 어려움을 겪고 있습니다.
수동으로 앞발에 주석을 달았습니다(RF=우측 전면, RH=우측 후면, LF=좌측 전면, LH=좌측 후면).
보시다시피 반복되는 패턴이 있으며 거의 모든 측정값에서 반복됩니다.다음은 수동으로 주석을 추가한 6가지 평가판에 대한 프레젠테이션 링크입니다.
처음에는 휴리스틱을 사용하여 다음과 같이 정렬하는 것을 참조하십시오.
- 앞발과 뒷발 사이의 체중 지지율은 약 60-40%입니다.
- 뒷발은 일반적으로 표면이 더 작습니다.
- 앞발은 (종종) 공간적으로 왼쪽과 오른쪽으로 나뉩니다.
하지만 저는 제 휴리스틱에 대해 약간 회의적입니다. 왜냐하면 제가 생각하지 못했던 변화를 만나자마자 실패할 것이기 때문입니다.그들은 또한 그들만의 규칙을 가지고 있는 절뚝거리는 개들의 측정에 대처할 수 없을 것입니다.
게다가, Joe가 제안한 주석은 때때로 엉망이 되어 발이 실제로 어떻게 생겼는지 고려하지 않습니다.
발 안의 피크 감지에 대한 질문에 대한 답변을 바탕으로, 저는 발을 분류할 수 있는 더 발전된 해결책이 있기를 바랍니다.특히 압력 분포와 그 진행이 각각의 다른 발톱마다 다르기 때문에 거의 지문과 같습니다.그냥 발생순으로 분류하는 것이 아니라 이것을 이용해 제 발을 군집화할 수 있는 방법이 있었으면 좋겠습니다.
그래서 저는 그들의 대응하는 발로 결과를 분류하는 더 나은 방법을 찾고 있습니다.
도전에 도전한 사람들을 위해, 저는 각 발톱의 압력 데이터(측정 단위로 번들링됨)와 위치(접시 위의 위치 및 시간 내의 위치)를 설명하는 슬라이스가 포함된 모든 슬라이스 배열이 포함된 사전을 피클에 절였습니다.
To clarfiy: walk_slitted_data는 측정값의 이름인 ['ser_3', 'ser_2', 'sel_1', 'sel_3']이 포함된 사전입니다.각 측정에는 추출된 영향을 나타내는 다른 사전 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]('sel_1'의 예)이 포함되어 있습니다.
또한 발이 부분적으로 측정되는 위치(공간 또는 시간)와 같은 '거짓' 영향은 무시할 수 있습니다.패턴을 인식하는 데 도움이 될 수 있지만 분석되지 않기 때문에 유용합니다.
그리고 관심 있는 사람들을 위해, 저는 그 프로젝트에 관한 모든 업데이트를 블로그에 쓰고 있습니다!
좋아!저는 마침내 무언가가 꾸준히 작동할 수 있게 되었습니다!이 문제 때문에 며칠 동안 꼼짝 못하게 됐어요재밌는 거.답변이 길어져서 죄송합니다만, 좀 더 자세히 설명해야 할 것이 있습니다...(비스팸 스택 오버플로 응답 중 가장 긴 응답 기록을 세울 수도 있지만!)
참고로, 저는 아이보가 원래 질문에서 링크를 제공한 전체 데이터 세트를 사용하고 있습니다.이것은 일련의 rar 파일(개당 1개)로, 각각 여러 개의 서로 다른 실험 실행이 ASCII 배열로 저장됩니다.독립 실행형 코드 예제를 이 질문에 복사하여 붙여넣는 대신 전체 독립 실행형 코드가 있는 비트 버킷 수은 저장소가 있습니다.복제할 수 있습니다.
hg clone https://joferkington@bitbucket.org/joferkington/paw-analysis
개요
질문에서 언급한 것처럼 문제에 접근하는 데는 기본적으로 두 가지 방법이 있습니다.저는 사실 두 가지를 다른 방식으로 사용할 것입니다.
- 어느 쪽이 어느 쪽인지 결정하려면 (시간 및 공간) 발 충격의 순서를 사용합니다.
- 순수하게 모양을 기준으로 "파우프린트"를 식별해 보십시오.
기본적으로 첫 번째 방법은 위의 아이보의 질문에 나온 사다리꼴 모양의 패턴을 따라 개의 발로 작동하지만, 발이 그 패턴을 따르지 않을 때마다 실패합니다.작동하지 않을 때 프로그램으로 탐지하는 것은 매우 쉽습니다.
따라서, 우리는 훈련 데이터 세트를 구축하기 위해 작동했던 측정값을 사용하여 (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) 각 paw print의 픽셀 수가 동일하고 b) 압력 값이 표준화되도록 크기를 조정해야 합니다.이를 위해 20x20 그리드에 각 paw print를 다시 샘플링하고 paw impact의 최대값, 최소값 및 평균 압력 값을 기준으로 압력 값을 다시 조정했습니다.
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차원 벡터와 비교할 수 있습니다.
불행하게도, 우리가 "정상적인" 감독 분류 알고리즘(즉, 간단한 거리를 사용하여 4개의 패턴 중 어느 것이 특정한 발톱 자국에 가장 가까운지 찾는 것)을 사용하면, 그것은 일관되게 작동하지 않습니다.사실, 그것은 훈련 데이터 세트에서 무작위 확률보다 훨씬 더 나은 것은 아닙니다.
이것은 이미지 인식에서 일반적인 문제입니다.입력 데이터의 높은 차원성과 이미지의 다소 "퍼지" 특성(즉, 인접 픽셀은 공분산이 높음)으로 인해 템플릿 이미지와 이미지의 차이를 보는 것만으로는 모양의 유사성을 매우 잘 측정할 수 없습니다.
고유발톱
이 문제를 피하기 위해 우리는 (얼굴 인식의 "고유한 얼굴"과 마찬가지로) 일련의 "고유한 발톱"을 만들고 각 발톱을 이러한 고유한 발톱의 조합으로 설명해야 합니다.이는 주성분 분석과 동일하며 기본적으로 데이터의 차원을 줄이는 방법을 제공하므로 거리가 모양의 좋은 측도가 됩니다.
우리는 차원(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
"발톱"입니다.
이것들을 사용하기 위해, 우리는 단순히 (20x20 이미지가 아닌 400차원 벡터로) 각 발 이미지를 기본 벡터로 점을 찍습니다(즉, 행렬 곱셈).이를 통해 이미지를 분류하는 데 사용할 수 있는 50차원 벡터(기본 벡터당 하나의 요소)를 얻을 수 있습니다.20x20 이미지를 각 "템플릿" 발톱의 20x20 이미지와 비교하는 대신, 50차원 변환된 이미지를 각 50차원 변환된 템플릿 발톱과 비교합니다.이는 각 발가락이 정확히 어떻게 배치되는지 등의 작은 변화에도 훨씬 덜 민감하며, 기본적으로 문제의 차원을 관련된 차원으로만 줄입니다.
고유발톱기반발톱분류
이제 우리는 각 다리에 대한 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()]
다음은 몇 가지 결과입니다.
남은 문제
아직도 몇 가지 문제가 있습니다. 특히 개들은 너무 작아서 명확한 발자국을 낼 수 없습니다.(센서의 해상도에서 발가락이 더 명확하게 분리되기 때문에 대형견에게 가장 적합합니다.)또한, 사다리꼴 패턴 기반 시스템에서는 부분적인 발자국이 인식되지 않지만, 이 시스템에서는 부분적인 발자국이 인식될 수 있습니다.
그러나 고유 발톱 분석은 본질적으로 거리 메트릭을 사용하기 때문에, 우리는 발톱을 양방향으로 분류할 수 있고, 고유 발톱 분석의 "코드북"에서 가장 작은 거리가 어떤 임계값을 초과할 때 사다리꼴 패턴 기반 시스템으로 다시 분류할 수 있습니다.아직 구현하지 못했습니다.
휴... 정말 길었군요!그런 재미있는 질문을 한 아이보에게 경의를 표합니다!
순수하게 지속시간에 기초한 정보를 사용하면 모델링 운동학, 즉 역 운동학의 기술을 적용할 수 있다고 생각합니다.방향, 길이, 지속 시간 및 총 무게와 결합하여 어느 정도의 주기성을 제공하며, 저는 여러분의 "발톱 분류" 문제를 해결하기 위한 첫 단계가 될 수 있기를 바랍니다.
이 모든 데이터를 사용하여 경계가 있는 다각형(또는 튜플) 목록을 만들 수 있으며, 이를 사용하여 단계 크기별로 정렬한 다음 이동도 [색인]별로 정렬할 수 있습니다.
테스트를 수행하는 정비사가 수동으로 첫 번째 포(또는 처음 두 개)를 입력하도록 할 수 있습니까?프로세스는 다음과 같습니다.
- 기술자에게 단계 순서 이미지를 보여주고 첫 번째 발톱에 주석을 달도록 요구합니다.
- 첫 번째 발톱에 따라 다른 발톱에 라벨을 붙이고 정비사가 수정하거나 테스트를 다시 실행할 수 있도록 합니다.이것은 절뚝거리거나 다리가 세 개인 개를 허용합니다.
언급URL : https://stackoverflow.com/questions/4502656/how-to-sort-my-paws
'programing' 카테고리의 다른 글
큰 MySQL 제품 내역 테이블 파티셔닝? (0) | 2023.09.03 |
---|---|
점이 없는 파일(확장자가 없는 모든 파일)을 gitignore 파일에 추가하려면 어떻게 해야 합니까? (0) | 2023.09.03 |
appcompat-v7:21.0.0': 지정된 이름과 일치하는 리소스를 찾을 수 없습니다. 특성 'android:actionModeShareDrawable' (0) | 2023.08.29 |
조인 마리애드브 제한 수 (0) | 2023.08.29 |
C++ 문자열에서 상수가 아닌 C 문자열을 다시 받을 수 있습니까? (0) | 2023.08.29 |