[python] 2D 어레이에서 피크 검출

나는 개 발 아래 압력을 측정하는 동물 병원을 돕고 있습니다. 데이터 분석에 Python을 사용하고 이제 발을 (해부) 하위 영역으로 나누려고 노력하고 있습니다.

나는 각 발의 2D 배열을 만들었습니다. 각 발은 시간이 지남에 따라 발에 의해로드 된 각 센서의 최대 값으로 구성됩니다. 다음은 한 발의 예입니다. Excel에서 ‘감지’할 영역을 그렸습니다. 이것들은 로컬 최대 값을 가진 센서 주위에 2 x 2 상자이며, 가장 큰 합계를 갖습니다.

대체 텍스트

그래서 나는 약간의 실험을 시도하고 단순히 각 열과 행의 최대 값을 찾기로 결정했습니다 (발 모양으로 인해 한 방향으로 볼 수 없음). 이것은 개별 발가락의 위치를 ​​상당히 잘 감지하는 것처럼 보이지만 인접한 센서를 표시합니다.

대체 텍스트

그렇다면 파이썬에이 최대 값 중 내가 원하는 최대 값을 알려주는 가장 좋은 방법은 무엇입니까?

참고 : 2×2 사각형은 별도의 발가락이어야하므로 겹쳐 질 수 없습니다!

또한 편의를 위해 2×2를 사용했지만 더 이상 고급 솔루션을 환영하지만, 저는 단순히 인간 운동 과학자이기 때문에 실제 프로그래머 나 수학자가 아니므로 간단하게 유지하십시오.

다음은 로드 할 수 있는 버전입니다np.loadtxt


결과

그래서 @jextee의 솔루션을 시도했습니다 (아래 결과 참조). 보시다시피, 앞발에서 매우 효과적이지만 뒷다리에서는 잘 작동하지 않습니다.

보다 구체적으로, 네 번째 발가락 인 작은 피크를 인식 할 수 없습니다. 이것은 현재 위치를 고려하지 않고 루프가 가장 낮은 값으로 하향 보인다는 사실에 내재되어 있습니다.

누구든지 @jextee의 알고리즘을 조정하여 4 번째 발가락도 찾을 수 있도록 알고 있습니까?

대체 텍스트

아직 다른 시험을 처리하지 않았으므로 다른 샘플을 제공 할 수 없습니다. 그러나 이전에 제공된 데이터는 각 발의 평균이었습니다. 이 파일은 플레이트와 접촉 한 순서대로 최대 9 발의 데이터를 가진 배열입니다.

이 이미지는 판 위에 공간적으로 어떻게 퍼져 있는지 보여줍니다.

대체 텍스트

최신 정보:

내가 관심있는 사람들을위한 블로그를 설정 하고 나는 원시 측정 모두와 스카이 드라이브 설정을 가지고있다. 더 많은 데이터를 요청하는 사람에게는 더 많은 힘이 필요합니다!


새로운 업데이트:

그래서 발 감지발 정렬 에 관한 질문에 대한 도움을 얻은 후에 마침내 모든 발에 대한 발가락 감지를 확인할 수있었습니다! 결과적으로, 그것은 내 자신의 예에서와 같은 크기의 발 외에는 잘 작동하지 않습니다. 뒤늦은 시각에서 2×2를 임의로 선택하는 것은 내 잘못입니다.

손톱이 발가락으로 인식되고 ‘발 뒤꿈치’가 너무 넓어서 두 번 인식됩니다!

대체 텍스트

발이 너무 커서 겹치지 않고 2×2 크기를 사용하면 발가락이 두 번 감지됩니다. 다른 방법으로, 작은 개에서는 종종 5 번째 발가락을 찾지 못합니다 .2×2 영역이 너무 커서 원인이되는 것 같습니다.

모든 측정에서 현재 솔루션을 시도한 후 거의 모든 작은 개에 대해 5 번째 발가락을 찾지 못했고 큰 개에 대한 영향의 50 % 이상에서 더 많은 것을 발견 할 수 있다는 놀라운 결론에 도달했습니다!

분명히 바꿔야합니다. 내 자신의 추측은 neighborhood작은 개의 경우 더 작고 큰 개의 경우 더 큰 것으로 크기를 변경하는 것입니다. 그러나 generate_binary_structure배열의 크기를 변경할 수는 없습니다.

따라서 다른 사람이 발가락 위치를 지정하는 데 더 나은 제안이 있기를 바라고 있습니다. 발 크기가 발가락 크기로되어 있습니까?



답변

로컬 최대 필터를 사용하여 피크를 감지했습니다 . 4 발의 첫 번째 데이터 세트에 대한 결과는 다음과 같습니다.
피크 검출 결과

나는 또한 9 발의 두 번째 데이터 세트에서 그것을 실행했으며 잘 작동했습니다 .

방법은 다음과 같습니다.

import numpy as np
from scipy.ndimage.filters import maximum_filter
from scipy.ndimage.morphology import generate_binary_structure, binary_erosion
import matplotlib.pyplot as pp

#for some reason I had to reshape. Numpy ignored the shape header.
paws_data = np.loadtxt("paws.txt").reshape(4,11,14)

#getting a list of images
paws = [p.squeeze() for p in np.vsplit(paws_data,4)]


def detect_peaks(image):
    """
    Takes an image and detect the peaks usingthe local maximum filter.
    Returns a boolean mask of the peaks (i.e. 1 when
    the pixel's value is the neighborhood maximum, 0 otherwise)
    """

    # define an 8-connected neighborhood
    neighborhood = generate_binary_structure(2,2)

    #apply the local maximum filter; all pixel of maximal value 
    #in their neighborhood are set to 1
    local_max = maximum_filter(image, footprint=neighborhood)==image
    #local_max is a mask that contains the peaks we are 
    #looking for, but also the background.
    #In order to isolate the peaks we must remove the background from the mask.

    #we create the mask of the background
    background = (image==0)

    #a little technicality: we must erode the background in order to 
    #successfully subtract it form local_max, otherwise a line will 
    #appear along the background border (artifact of the local maximum filter)
    eroded_background = binary_erosion(background, structure=neighborhood, border_value=1)

    #we obtain the final mask, containing only peaks, 
    #by removing the background from the local_max mask (xor operation)
    detected_peaks = local_max ^ eroded_background

    return detected_peaks


#applying the detection and plotting results
for i, paw in enumerate(paws):
    detected_peaks = detect_peaks(paw)
    pp.subplot(4,2,(2*i+1))
    pp.imshow(paw)
    pp.subplot(4,2,(2*i+2) )
    pp.imshow(detected_peaks)

pp.show()

scipy.ndimage.measurements.label마스크를 사용 하여 모든 별개의 오브젝트에 레이블을 지정하기 만하면됩니다. 그런 다음 개별적으로 게임을 즐길 수 있습니다.

배경 잡음이 없기 때문에이 방법이 잘 작동합니다. 그렇다면 배경에서 원하지 않는 다른 봉우리들을 감지 할 수 있습니다. 또 다른 중요한 요소는 이웃 의 크기입니다 . 피크 크기가 변하면 조정해야합니다 (대략 비례해야 함).


답변

해결책

데이터 파일 : paw.txt . 소스 코드:

from scipy import *
from operator import itemgetter

n = 5  # how many fingers are we looking for

d = loadtxt("paw.txt")
width, height = d.shape

# Create an array where every element is a sum of 2x2 squares.

fourSums = d[:-1,:-1] + d[1:,:-1] + d[1:,1:] + d[:-1,1:]

# Find positions of the fingers.

# Pair each sum with its position number (from 0 to width*height-1),

pairs = zip(arange(width*height), fourSums.flatten())

# Sort by descending sum value, filter overlapping squares

def drop_overlapping(pairs):
    no_overlaps = []
    def does_not_overlap(p1, p2):
        i1, i2 = p1[0], p2[0]
        r1, col1 = i1 / (width-1), i1 % (width-1)
        r2, col2 = i2 / (width-1), i2 % (width-1)
        return (max(abs(r1-r2),abs(col1-col2)) >= 2)
    for p in pairs:
        if all(map(lambda prev: does_not_overlap(p,prev), no_overlaps)):
            no_overlaps.append(p)
    return no_overlaps

pairs2 = drop_overlapping(sorted(pairs, key=itemgetter(1), reverse=True))

# Take the first n with the heighest values

positions = pairs2[:n]

# Print results

print d, "\n"

for i, val in positions:
    row = i / (width-1)
    column = i % (width-1)
    print "sum = %f @ %d,%d (%d)" % (val, row, column, i)
    print d[row:row+2,column:column+2], "\n"

사각형이 겹치지 않고 출력 됩니다. 귀하의 예에서와 동일한 영역이 선택된 것 같습니다.

일부 의견

까다로운 부분은 모든 2×2 제곱의 합을 계산하는 것입니다. 나는 당신이 그들 모두를 필요로한다고 가정 했으므로 약간 겹칠 수 있습니다. 슬라이스를 사용하여 원래 2D 배열에서 첫 번째 / 마지막 열과 행을 잘라낸 다음 모두 함께 겹쳐서 합계를 계산했습니다.

더 잘 이해하려면 3×3 배열을 이미징하십시오.

>>> a = arange(9).reshape(3,3) ; a
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

그런 다음 조각을 취할 수 있습니다.

>>> a[:-1,:-1]
array([[0, 1],
       [3, 4]])
>>> a[1:,:-1]
array([[3, 4],
       [6, 7]])
>>> a[:-1,1:]
array([[1, 2],
       [4, 5]])
>>> a[1:,1:]
array([[4, 5],
       [7, 8]])

이제 그것들을 다른 것 위에 쌓고 같은 위치에 요소를 합산한다고 상상해보십시오. 이 합계는 왼쪽 위 모서리가 같은 위치에있는 2×2 정사각형에 대해 정확히 같은 합계입니다.

>>> sums = a[:-1,:-1] + a[1:,:-1] + a[:-1,1:] + a[1:,1:]; sums
array([[ 8, 12],
       [20, 24]])

합계가 2×2 제곱을 초과 max하면 최대 값 sort또는 sorted을 찾거나 피크를 찾는 데 사용할 수 있습니다 .

피크의 위치를 ​​기억하기 위해 모든 값 (합)을 평평한 배열의 서수 위치와 결합합니다 (참조 zip). 그런 다음 결과를 인쇄 할 때 행 / 열 위치를 다시 계산합니다.

노트

2×2 사각형이 겹치도록 허용했습니다. 편집 된 버전은 겹치지 않는 사각형 만 결과에 나타나도록 일부를 필터링합니다.

손가락 선택 (아이디어)

또 다른 문제는 모든 피크에서 손가락이 될 가능성을 선택하는 방법입니다. 작동하거나 작동하지 않을 수있는 아이디어가 있습니다. 지금 당장 구현할 시간이 없으므로 의사 코드 만 있습니다.

앞 손가락이 거의 완벽한 원 안에 있으면 뒷 손가락이 그 원 안에 있어야한다는 것을 알았습니다. 또한 앞쪽 손가락의 간격이 거의 동일합니다. 이러한 휴리스틱 속성을 사용하여 손가락을 감지하려고 할 수 있습니다.

의사 코드 :

select the top N finger candidates (not too many, 10 or 12)
consider all possible combinations of 5 out of N (use itertools.combinations)
for each combination of 5 fingers:
    for each finger out of 5:
        fit the best circle to the remaining 4
        => position of the center, radius
        check if the selected finger is inside of the circle
        check if the remaining four are evenly spread
        (for example, consider angles from the center of the circle)
        assign some cost (penalty) to this selection of 4 peaks + a rear finger
        (consider, probably weighted:
             circle fitting error,
             if the rear finger is inside,
             variance in the spreading of the front fingers,
             total intensity of 5 peaks)
choose a combination of 4 peaks + a rear peak with the lowest penalty

이것은 무차별 접근 방식입니다. N이 상대적으로 작 으면 가능하다고 생각합니다. N = 12의 경우, C_12 ^ 5 = 792 조합, 배 손가락을 선택하는 5 가지 방법이 있으므로 모든 발에 대해 3960 개의 사례를 평가합니다.


답변

이것은 이미지 등록 문제 입니다. 일반적인 전략은 다음과 같습니다.

  • 알려진 예 또는 데이터에 대한 사전 이 있습니다.
  • 데이터를 예제에 맞추거나 예제를 데이터에 맞추십시오.
  • 데이터가 처음에 대략적으로 정렬 되면 도움이됩니다 .

다음은 거칠고 준비된 접근 방식입니다 .

  • 대략 원하는 위치에서 5 개의 발가락 좌표로 시작하십시오.
  • 각각이 반복적으로 언덕 꼭대기까지 올라갑니다. 즉, 현재 위치에서 주어진 값이 현재 픽셀보다 큰 경우 최대 인접 픽셀로 이동합니다. 발가락 좌표의 움직임이 멈 추면 멈 춥니 다.

방향 문제를 해결하기 위해 기본 방향 (북쪽, 북동쪽 등)에 대해 8 개 정도의 초기 설정을 지정할 수 있습니다. 각각을 개별적으로 실행하고 두 개 이상의 발가락이 같은 픽셀에서 끝나는 결과를 버리십시오. 나는 이것에 대해 더 생각할 것이지만, 이런 종류의 일은 여전히 ​​이미지 처리에 대해 연구되고 있습니다. 정답은 없습니다!

약간 더 복잡한 아이디어 : (가중치) K- 평균 군집. 그렇게 나쁘진 않네.

  • 5 개의 발가락 좌표로 시작하지만 이제는 “클러스터 센터”입니다.

그런 다음 수렴 될 때까지 반복하십시오.

  • 각 픽셀을 가장 가까운 클러스터에 할당하십시오 (각 클러스터에 대한 목록을 작성하십시오).
  • 각 군집의 질량 중심을 계산하십시오. 각 군집에 대해 Sum (좌표 * 강도 값) / Sum (좌표)입니다.
  • 각 클러스터를 새로운 질량 중심으로 이동하십시오.

이 방법은 거의 확실히 더 나은 결과를 제공하며 발가락을 식별하는 데 도움이 될 수있는 각 클러스터의 질량을 얻습니다.

(먼저 클러스터 수를 미리 지정했습니다. 클러스터링을 사용하면 밀도를 한 가지 방법으로 지정해야합니다.이 경우 적절한 클러스터 수를 선택하거나 클러스터 반경을 선택하고 종료 수를 확인하십시오. 후자의 예는 평균 이동 입니다.)

구현 세부 사항 또는 기타 세부 사항이 부족하여 죄송합니다. 코드를 작성했지만 마감일이 있습니다. 다음 주까지 다른 일이 없으면 알려 주시면 기회를 드리겠습니다.


답변

지속적인 상 동성을 사용하여 데이터 세트를 분석하면 다음과 같은 결과가 나타납니다 (확대하려면 클릭하십시오).

결과

이것이이 SO 답변에 설명 된 피크 검출 방법의 2D 버전입니다 . 위 그림은 지속성에 따라 정렬 된 0 차원 영구 상 동성 클래스를 보여줍니다.

scipy.misc.imresize ()를 사용하여 원래 데이터 세트를 2 배로 업 스케일했습니다. 그러나 네 발을 하나의 데이터 집합으로 간주했습니다. 4 개로 나누면 문제가 더 쉬워집니다.

방법론.
이 매우 간단한 아이디어는 각 픽셀에 레벨을 할당하는 함수의 함수 그래프를 고려하십시오. 다음과 같이 보입니다 :

3D 함수 그래프

이제 높이 255의 수위가 지속적으로 낮아지는 수위를 고려하십시오. 지역 최대 섬에서 팝업 (출생). 안장 지점에서 두 개의 섬이 합쳐집니다. 우리는 더 낮은 섬이 더 높은 섬 (죽음)과 합쳐지는 것을 고려합니다. 소위 지속성 다이어그램 (우리 섬의 0 차원 상 동성 등급)은 모든 섬의 사망률을 나타냅니다.

지속성 다이어그램

섬 의 지속성 은 출생 수준과 죽음 수준의 차이입니다. 회색 주 대각선에 대한 점의 수직 거리 이 그림은 지속성을 감소시켜 섬을 표시합니다.

첫 번째 그림은 섬의 출생지입니다. 이 방법은 국소 최대 값을 제공 할뿐만 아니라 위에서 언급 한 지속성에 의해 “의미”를 정량화합니다. 그런 다음 지속성이 너무 낮은 모든 섬을 필터링합니다. 그러나 귀하의 예에서 모든 섬 (즉, 모든 지역 최대)은 원하는 피크입니다.

파이썬 코드는 여기 에서 찾을 수 있습니다 .


답변

이 문제는 물리학 자에 의해 어느 정도 깊이 연구되었습니다. ROOT 에는 좋은 구현이 있습니다 . 상기 봐 TSpectrum의 클래스 (특히 TSpectrum2 사건에 대한) 그들에 대한 설명서.

참고 문헌 :

  1. M.Morhac 등 : 다차원 일치 감마선 스펙트럼의 배경 제거 방법. 물리학 연구 A 401 (1997) 113-132의 핵기구 및 방법.
  2. M.Morhac et al .: 효율적인 1 차원 및 2 차원 금 deconvolution 및 감마선 스펙트럼 분해에 적용. 물리학 연구 A 401 (1997) 385-408에있는 핵기구 및 방법.
  3. M.Morhac et al .: 다차원 일치 감마선 스펙트럼에서 피크 식별. 연구 물리학의 핵기구 및 방법 A 443 (2000), 108-125.

… 그리고 NIM 구독이없는 사람들을 위해 :


답변

아이디어는 다음과 같습니다. 이미지의 (이산적인) 라플라시안을 계산합니다. 나는 그것이 원래 이미지보다 더 극적인 방식으로 최대에서 (부정적이고) 클 것으로 기대합니다. 따라서 최대 값을 찾기가 더 쉬울 수 있습니다.

또 다른 아이디어는 다음과 같습니다. 고압 스폿의 일반적인 크기를 알고 있다면 먼저 동일한 크기의 가우시안으로 이미지를 변환하여 이미지를 부드럽게 할 수 있습니다. 이렇게하면 처리하기 더 간단한 이미지를 얻을 수 있습니다.


답변

내 머리 위로 몇 가지 아이디어가 있습니다.

  • 스캔의 기울기 (파생)를 취하여 잘못된 호출을 제거하는지 확인하십시오.
  • 극댓값을 최대한 활용하다

OpenCV를 살펴보고 싶을 수도 있지만 꽤 괜찮은 Python API가 있으며 유용한 기능이있을 수 있습니다.