Nice programing

2D 어레이에서 피크 감지

nicepro 2020. 9. 28. 10:01
반응형

2D 어레이에서 피크 감지


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

저는 시간이 지남에 따라 발에 의해로드 된 각 센서의 최대 값으로 구성된 각 발의 2D 배열을 만들었습니다. 다음은 Excel을 사용하여 '감지'하려는 영역을 그리는 한 발의 예입니다. 이들은 함께 가장 큰 합계를 갖는 로컬 최대 값을 갖는 센서 주변의 2x2 상자입니다.

대체 텍스트

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

대체 텍스트

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

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

또한 편의상 2x2를 사용했습니다. 더 발전된 솔루션은 환영합니다.하지만 저는 단순히 인간 운동 과학자이므로 실제 프로그래머 나 수학자가 아니므로 '단순'으로 유지하십시오.

로드 할 수 있는 버전은 다음과 같습니다.np.loadtxt


결과

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

좀 더 구체적으로 말하면 네 번째 발가락 인 작은 봉우리를 인식 할 수 없습니다. 이것은 루프가 이것이 어디에 있는지 고려하지 않고 가장 낮은 값을 향해 하향식으로 보인다는 사실에 내재되어 있습니다.

누구든지 @jextee의 알고리즘을 조정하는 방법을 알고 있으므로 네 번째 발가락도 찾을 수 있습니까?

대체 텍스트

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

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

대체 텍스트

최신 정보:

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


새로운 업데이트:

그래서 발 감지발 분류 에 관한 질문에 대한 도움을받은 후 마침내 모든 발의 발가락 감지를 확인할 수있었습니다! 내 자신의 예에서와 같은 크기의 발을 제외하고는 아무것도 잘 작동하지 않습니다. 돌이켜 보면 2x2를 임의로 선택한 것은 내 잘못입니다.

여기에 잘못된 부분에 대한 좋은 예가 있습니다. 손톱은 발가락으로 인식되고 '뒤꿈치'는 너무 넓어서 두 번 인식됩니다!

대체 텍스트

발이 너무 커서 겹치지 않는 2x2 크기를 사용하면 일부 발가락이 두 번 감지됩니다. 반대로, 작은 개에서는 5 번째 발가락을 찾지 못하는 경우가 많습니다. 2x2 영역이 너무 커서 원인이라고 생각합니다.

내 모든 측정에 대해 현재 솔루션을 시도한거의 모든 작은 개에 대해 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"

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

일부 댓글

까다로운 부분은 모든 2x2 제곱의 합을 계산하는 것입니다. 나는 당신이 그들 모두가 필요하다고 생각했기 때문에 약간 겹칠 수 있습니다. 슬라이스를 사용하여 원래 2D 배열에서 첫 번째 / 마지막 열과 행을 자른 다음 모두 겹쳐서 합계를 계산했습니다.

더 잘 이해하려면 3x3 어레이를 이미징하십시오.

>>> 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]])

이제 하나를 다른 위에 쌓고 같은 위치에있는 요소를 합산한다고 상상해보십시오. 이 합계는 동일한 위치에 왼쪽 상단 모서리가있는 2x2 정사각형에 대해 정확히 동일한 합계입니다.

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

합계가 2x2 제곱 이상이면을 사용 max하여 최대 값 sort또는 sorted을 찾거나 피크를 찾을 수 있습니다.

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

메모

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

손가락 선택 (아이디어)

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

앞 손가락이 거의 완전한 원에 머물러 있다면 뒷 손가락이 그 원 안에 있어야한다는 것을 알았습니다. 또한 앞쪽 손가락의 간격은 다소 균등합니다. 이러한 휴리스틱 속성을 사용하여 손가락을 감지 할 수 있습니다.

의사 코드 :

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 (좌표)
  • 각 클러스터를 새로운 질량 중심으로 이동합니다.

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

(다시 한 번, 클러스터 수를 미리 지정했습니다. 클러스터링을 사용하려면 밀도를 한 가지 또는 다른 방식으로 지정해야합니다.이 경우에 적합한 클러스터 수를 선택하거나 클러스터 반경을 선택하고 끝낼 수를 확인하십시오. 후자의 예는 평균 이동 입니다.)

구현 세부 정보 또는 기타 세부 사항이 부족하여 죄송합니다. 이 코드를 작성했지만 마감일이 있습니다. 다음 주까지 아무런 효과가 없다면 알려 주시면 한 번 시도해 보겠습니다.


이 문제는 물리학 자들에 의해 어느 정도 깊이 연구되었습니다. ROOT에 좋은 구현이 있습니다 . TSpectrum 클래스 (특히 귀하의 경우에는 TSpectrum2 )와 해당 문서를 살펴보십시오 .

참조 :

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

... NIM에 대한 구독에 액세스 할 수없는 사람들을 위해 :


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

여기에 또 다른 아이디어가 있습니다. 고압 지점의 일반적인 크기를 알고 있다면 먼저 동일한 크기의 가우스로 이미지를 컨볼 루션하여 이미지를 부드럽게 할 수 있습니다. 이렇게하면 처리 할 이미지가 더 간단해질 수 있습니다.


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

결과

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

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

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

3D 함수 그래프

이제 계속해서 낮은 수위로 내려가는 높이 255의 수위를 고려하십시오. 로컬 맥시마 섬에서 팝업 (출생). 안장 지점에서 두 개의 섬이 병합됩니다. 우리는 더 낮은 섬이 더 높은 섬 (죽음)과 합쳐지는 것으로 간주합니다. 소위 지속성 다이어그램 (0 차원 상 동성 클래스, 우리 섬)은 모든 섬의 출생 가치에 대한 죽음을 묘사합니다.

지속성 다이어그램

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

첫 번째 그림은 섬의 출생 위치를 보여줍니다. 이 방법은 국부 최대 값을 제공 할뿐만 아니라 위에서 언급 한 지속성에 의해 "중요도"를 정량화합니다. 그런 다음 너무 낮은 지속성으로 모든 섬을 걸러냅니다. 그러나 귀하의 예에서 모든 섬 (즉, 모든 로컬 최대 값)은 찾고있는 피크입니다.

Python 코드는 여기 에서 찾을 수 있습니다 .


내 머릿속에서 몇 가지 아이디어가 있습니다.

  • 스캔의 기울기 (미분)를 가져 와서 잘못된 호출이 제거되는지 확인합니다.
  • 극댓값을 취하다

OpenCV를 살펴보고 싶을 수도 있습니다 . 꽤 괜찮은 Python API가 있고 유용하다고 생각되는 일부 기능이있을 수 있습니다.


지금까지 계속 진행할 수있을만큼 충분하다고 확신하지만 k- 평균 클러스터링 방법을 사용하는 것이 좋습니다. k- 평균은 비지도 클러스터링 알고리즘으로, 데이터를 (모든 차원에서-3D로 수행합니다) 별개의 경계를 가진 k 클러스터로 배열합니다. 이 송곳니가 얼마나 많은 발가락을 가지고 있는지 정확히 알고 있기 때문에 여기가 좋습니다.

또한 정말 멋진 Scipy로 구현되어 있습니다 ( http://docs.scipy.org/doc/scipy/reference/cluster.vq.html ).

다음은 3D 클러스터를 공간적으로 해결하기 위해 수행 할 수있는 작업의 예입니다. 여기에 이미지 설명 입력

원하는 것은 약간 다르지만 (2D 및 압력 값 포함) 그래도 시도해 볼 수 있다고 생각합니다.


원시 데이터에 감사드립니다. 나는 기차에 있고 이것은 내가 얻은 한 (내 정류장이 다가오고 있음)입니다. regexps로 txt 파일을 마사지하고 시각화를 위해 일부 자바 스크립트가있는 html 페이지에 넣었습니다. 저와 같은 일부 사람들은 파이썬보다 쉽게 ​​해킹 할 수 있기 때문에 여기서 공유하고 있습니다.

좋은 접근 방식은 크기와 회전이 불변 할 것이라고 생각하며 다음 단계는 가우스 혼합을 조사하는 것입니다. (각 발 패드는 가우시안의 중심이 됨).

    <html>
<head>
    <script type="text/javascript" src="http://vis.stanford.edu/protovis/protovis-r3.2.js"></script> 
    <script type="text/javascript">
    var heatmap = [[[0,0,0,0,0,0,0,4,4,0,0,0,0],
[0,0,0,0,0,7,14,22,18,7,0,0,0],
[0,0,0,0,11,40,65,43,18,7,0,0,0],
[0,0,0,0,14,61,72,32,7,4,11,14,4],
[0,7,14,11,7,22,25,11,4,14,65,72,14],
[4,29,79,54,14,7,4,11,18,29,79,83,18],
[0,18,54,32,18,43,36,29,61,76,25,18,4],
[0,4,7,7,25,90,79,36,79,90,22,0,0],
[0,0,0,0,11,47,40,14,29,36,7,0,0],
[0,0,0,0,4,7,7,4,4,4,0,0,0]
],[
[0,0,0,4,4,0,0,0,0,0,0,0,0],
[0,0,11,18,18,7,0,0,0,0,0,0,0],
[0,4,29,47,29,7,0,4,4,0,0,0,0],
[0,0,11,29,29,7,7,22,25,7,0,0,0],
[0,0,0,4,4,4,14,61,83,22,0,0,0],
[4,7,4,4,4,4,14,32,25,7,0,0,0],
[4,11,7,14,25,25,47,79,32,4,0,0,0],
[0,4,4,22,58,40,29,86,36,4,0,0,0],
[0,0,0,7,18,14,7,18,7,0,0,0,0],
[0,0,0,0,4,4,0,0,0,0,0,0,0],
],[
[0,0,0,4,11,11,7,4,0,0,0,0,0],
[0,0,0,4,22,36,32,22,11,4,0,0,0],
[4,11,7,4,11,29,54,50,22,4,0,0,0],
[11,58,43,11,4,11,25,22,11,11,18,7,0],
[11,50,43,18,11,4,4,7,18,61,86,29,4],
[0,11,18,54,58,25,32,50,32,47,54,14,0],
[0,0,14,72,76,40,86,101,32,11,7,4,0],
[0,0,4,22,22,18,47,65,18,0,0,0,0],
[0,0,0,0,4,4,7,11,4,0,0,0,0],
],[
[0,0,0,0,4,4,4,0,0,0,0,0,0],
[0,0,0,4,14,14,18,7,0,0,0,0,0],
[0,0,0,4,14,40,54,22,4,0,0,0,0],
[0,7,11,4,11,32,36,11,0,0,0,0,0],
[4,29,36,11,4,7,7,4,4,0,0,0,0],
[4,25,32,18,7,4,4,4,14,7,0,0,0],
[0,7,36,58,29,14,22,14,18,11,0,0,0],
[0,11,50,68,32,40,61,18,4,4,0,0,0],
[0,4,11,18,18,43,32,7,0,0,0,0,0],
[0,0,0,0,4,7,4,0,0,0,0,0,0],
],[
[0,0,0,0,0,0,4,7,4,0,0,0,0],
[0,0,0,0,4,18,25,32,25,7,0,0,0],
[0,0,0,4,18,65,68,29,11,0,0,0,0],
[0,4,4,4,18,65,54,18,4,7,14,11,0],
[4,22,36,14,4,14,11,7,7,29,79,47,7],
[7,54,76,36,18,14,11,36,40,32,72,36,4],
[4,11,18,18,61,79,36,54,97,40,14,7,0],
[0,0,0,11,58,101,40,47,108,50,7,0,0],
[0,0,0,4,11,25,7,11,22,11,0,0,0],
[0,0,0,0,0,4,0,0,0,0,0,0,0],
],[
[0,0,4,7,4,0,0,0,0,0,0,0,0],
[0,0,11,22,14,4,0,4,0,0,0,0,0],
[0,0,7,18,14,4,4,14,18,4,0,0,0],
[0,4,0,4,4,0,4,32,54,18,0,0,0],
[4,11,7,4,7,7,18,29,22,4,0,0,0],
[7,18,7,22,40,25,50,76,25,4,0,0,0],
[0,4,4,22,61,32,25,54,18,0,0,0,0],
[0,0,0,4,11,7,4,11,4,0,0,0,0],
],[
[0,0,0,0,7,14,11,4,0,0,0,0,0],
[0,0,0,4,18,43,50,32,14,4,0,0,0],
[0,4,11,4,7,29,61,65,43,11,0,0,0],
[4,18,54,25,7,11,32,40,25,7,11,4,0],
[4,36,86,40,11,7,7,7,7,25,58,25,4],
[0,7,18,25,65,40,18,25,22,22,47,18,0],
[0,0,4,32,79,47,43,86,54,11,7,4,0],
[0,0,0,14,32,14,25,61,40,7,0,0,0],
[0,0,0,0,4,4,4,11,7,0,0,0,0],
],[
[0,0,0,0,4,7,11,4,0,0,0,0,0],
[0,4,4,0,4,11,18,11,0,0,0,0,0],
[4,11,11,4,0,4,4,4,0,0,0,0,0],
[4,18,14,7,4,0,0,4,7,7,0,0,0],
[0,7,18,29,14,11,11,7,18,18,4,0,0],
[0,11,43,50,29,43,40,11,4,4,0,0,0],
[0,4,18,25,22,54,40,7,0,0,0,0,0],
[0,0,4,4,4,11,7,0,0,0,0,0,0],
],[
[0,0,0,0,0,7,7,7,7,0,0,0,0],
[0,0,0,0,7,32,32,18,4,0,0,0,0],
[0,0,0,0,11,54,40,14,4,4,22,11,0],
[0,7,14,11,4,14,11,4,4,25,94,50,7],
[4,25,65,43,11,7,4,7,22,25,54,36,7],
[0,7,25,22,29,58,32,25,72,61,14,7,0],
[0,0,4,4,40,115,68,29,83,72,11,0,0],
[0,0,0,0,11,29,18,7,18,14,4,0,0],
[0,0,0,0,0,4,0,0,0,0,0,0,0],
]
];
</script>
</head>
<body>
    <script type="text/javascript+protovis">    
    for (var a=0; a < heatmap.length; a++) {
    var w = heatmap[a][0].length,
    h = heatmap[a].length;
var vis = new pv.Panel()
    .width(w * 6)
    .height(h * 6)
    .strokeStyle("#aaa")
    .lineWidth(4)
    .antialias(true);
vis.add(pv.Image)
    .imageWidth(w)
    .imageHeight(h)
    .image(pv.Scale.linear()
        .domain(0, 99, 100)
        .range("#000", "#fff", '#ff0a0a')
        .by(function(i, j) heatmap[a][j][i]));
vis.render();
}
</script>
  </body>
</html>

대체 텍스트


물리학 자의 솔루션 :
위치로 식별되는 5 개의 발 마커를 정의 X_i하고 임의의 위치로 초기화합니다. 발의 위치에있는 마커의 위치에 대한 일부 보상과 마커의 겹침에 대한 일부 처벌을 결합하는 일부 에너지 기능을 정의하십시오. 의 말을하자:

E(X_i;S)=-Sum_i(S(X_i))+alfa*Sum_ij (|X_i-Xj|<=2*sqrt(2)?1:0)

( S(X_i)2x2 정사각형 주위의 평균 힘 X_i, alfa실험적으로 정점에 도달 할 매개 변수)

이제 Metropolis-Hastings 마술을 할 시간입니다.
1. 임의의 마커를 선택하고 임의의 방향으로 한 픽셀 씩 이동합니다.
2.이 움직임이 야기한 에너지의 차이 인 dE를 계산합니다.
3. 0-1에서 균일 한 난수를 가져 와서 r이라고 부릅니다.
4. dE<0또는 exp(-beta*dE)>r이면 이동을 수락하고 1로 이동합니다. 그렇지 않은 경우 이동을 취소하고 1로 이동합니다.
마커가 발에 수렴 할 때까지이 작업을 반복해야합니다. 베타는 스캔을 제어하여 트레이드 오프를 최적화하므로 실험적으로 최적화해야합니다. 시뮬레이션 시간에 따라 지속적으로 증가 할 수도 있습니다 (시뮬레이션 된 어닐링).


다음은 대형 망원경에 대해 비슷한 작업을 할 때 사용한 또 다른 접근 방식입니다.

1) 가장 높은 픽셀을 검색합니다. 그런 다음 2x2에 가장 적합한 것을 검색하거나 (아마도 2x2 합계를 최대화 할 수 있음) 가장 높은 픽셀을 중심으로하는 4x4의 하위 영역 내부에 2d 가우스 피팅을 수행합니다.

그런 다음 찾은 2x2 픽셀을 피크 중심 주변에서 0 (또는 3x3)으로 설정합니다.

1)로 돌아가서 가장 높은 피크가 노이즈 임계 값 아래로 떨어질 때까지 반복하거나 필요한 모든 발가락을 갖습니다.


훈련 데이터를 생성 할 수 있다면 신경망으로 시도해 볼 가치가있을 것입니다.하지만 여기에는 손으로 주석이 달린 많은 샘플이 필요합니다.


대략적인 개요 ...

각 발 영역을 분리하기 위해 연결된 구성 요소 알고리즘을 사용하고 싶을 것입니다. wiki는 여기에 (일부 코드와 함께) 이에 대한 적절한 설명이 있습니다 : http://en.wikipedia.org/wiki/Connected_Component_Labeling

4 개 또는 8 개 연결을 사용할지 여부를 결정해야합니다. 개인적으로 대부분의 문제에 대해 6- 연결을 선호합니다. 어쨌든, 각 "발 프린트"를 연결된 영역으로 분리하면 영역을 반복하고 최대 값을 찾는 것이 충분히 쉬워야합니다. 최대치를 찾으면 주어진 "발가락"으로 식별하기 위해 미리 결정된 임계 값에 도달 할 때까지 영역을 반복적으로 확대 할 수 있습니다.

여기서 한 가지 미묘한 문제는 컴퓨터 비전 기술을 사용하여 무언가를 오른쪽 / 왼쪽 / 앞 / 뒤 발로 식별하기 시작하고 개별 발가락을보기 시작하자마자 회전, 기울임 및 변환을 고려해야한다는 것입니다. 이것은 소위 "순간"의 분석을 통해 이루어집니다. 비전 애플리케이션에서 고려해야 할 몇 가지 다른 순간이 있습니다.

중심 모멘트 : 변환 불변 정규화 모멘트 : 스케일링 및 변환 불변 hu 모멘트 : 변환, 스케일 및 회전 불변

순간에 대한 자세한 정보는 위키에서 "이미지 순간"을 검색하여 찾을 수 있습니다.


아마도 가우스 혼합 모델과 같은 것을 사용할 수 있습니다. 다음은 GMM을 수행하기위한 Python 패키지입니다 (Google 검색) http://www.ar.media.kyoto-u.ac.jp/members/david/softwares/em/


jetxee의 알고리즘을 사용하여 약간의 속임수를 쓸 수있는 것 같습니다. 그는 처음 세 개의 발가락을 잘 찾아 내고 있으며, 네 번째 발가락이 어디에 있는지 추측 할 수있을 것입니다.


흥미로운 문제입니다. 내가 시도 할 해결책은 다음과 같습니다.

  1. 2D 가우시안 마스크가있는 컨볼 루션과 같은 저역 통과 필터를 적용합니다. 이것은 많은 (아마도 부동 소수점은 아니지만) 값을 제공합니다.

  2. 각 발 패드 (또는 발가락)의 알려진 대략적인 반경을 사용하여 2D 비 최대 억제를 수행합니다.

이렇게하면 서로 가까이있는 여러 후보자가 없어도 최대 위치를 얻을 수 있습니다. 명확히하기 위해 1 단계에서 마스크의 반경은 2 단계에서 사용 된 반경과 유사해야합니다.이 반경은 선택 가능하거나 수의사가 사전에 명시 적으로 측정 할 수 있습니다 (연령 / 품종 등에 따라 다름).

제안 된 일부 솔루션 (평균 이동, 신경망 등)은 어느 정도 작동하지만 지나치게 복잡하고 이상적이지 않을 수 있습니다.


음, 여기에 간단하고 끔찍하지 않은 코드가 있지만이 크기의 데이터 세트에는 괜찮습니다.

import numpy as np
grid = np.array([[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
              [0,0,0,0,0,0,0,0,0.4,0.4,0.4,0,0,0],
              [0,0,0,0,0.4,1.4,1.4,1.8,0.7,0,0,0,0,0],
              [0,0,0,0,0.4,1.4,4,5.4,2.2,0.4,0,0,0,0],
              [0,0,0.7,1.1,0.4,1.1,3.2,3.6,1.1,0,0,0,0,0],
              [0,0.4,2.9,3.6,1.1,0.4,0.7,0.7,0.4,0.4,0,0,0,0],
              [0,0.4,2.5,3.2,1.8,0.7,0.4,0.4,0.4,1.4,0.7,0,0,0],
              [0,0,0.7,3.6,5.8,2.9,1.4,2.2,1.4,1.8,1.1,0,0,0],
              [0,0,1.1,5,6.8,3.2,4,6.1,1.8,0.4,0.4,0,0,0],
              [0,0,0.4,1.1,1.8,1.8,4.3,3.2,0.7,0,0,0,0,0],
              [0,0,0,0,0,0.4,0.7,0.4,0,0,0,0,0,0]])

arr = []
for i in xrange(grid.shape[0] - 1):
    for j in xrange(grid.shape[1] - 1):
        tot = grid[i][j] + grid[i+1][j] + grid[i][j+1] + grid[i+1][j+1]
        arr.append([(i,j),tot])

best = []

arr.sort(key = lambda x: x[1])

for i in xrange(5):
    best.append(arr.pop())
    badpos = set([(best[-1][0][0]+x,best[-1][0][1]+y)
                  for x in [-1,0,1] for y in [-1,0,1] if x != 0 or y != 0])
    for j in xrange(len(arr)-1,-1,-1):
        if arr[j][0] in badpos:
            arr.pop(j)


for item in best:
    print grid[item[0][0]:item[0][0]+2,item[0][1]:item[0][1]+2]

기본적으로 왼쪽 상단의 위치와 각 2x2 정사각형의 합계로 배열을 만들고 합계로 정렬합니다. 그런 다음 경합에서 가장 높은 합계를 가진 2x2 정사각형을 가져 와서 best배열에 넣고 방금 제거 된 2x2 정사각형의 일부를 사용한 다른 모든 2x2 정사각형을 제거합니다.

마지막 발 (첫 번째 사진의 맨 오른쪽에 가장 작은 합이있는 것)을 제외하고는 잘 작동하는 것 같습니다. 합이 더 큰 두 개의 다른 적격 한 2x2 사각형이 있다는 것이 밝혀졌습니다. 서로). 그들 중 하나는 여전히 2x2 사각형에서 하나의 사각형을 선택하지만 다른 하나는 왼쪽에 있습니다. 다행히도 운 좋게도 원하는 것을 더 많이 선택하는 것으로 보이지만, 실제로 원하는 것을 항상 얻기 위해 다른 아이디어를 사용해야 할 수도 있습니다.


파이썬을 사용하여 이미지에서 로컬 최대 값을 찾는 좋은 옵션이 있다고 말하고 싶습니다.

from skimage.feature import peak_local_max

또는 skimage 0.8.0의 경우

from skimage.feature.peak import peak_local_max

http://scikit-image.org/docs/0.8.0/api/skimage.feature.peak.html


순진한 접근 방식으로도 충분할 수 있습니다. 비행기에있는 모든 2x2 정사각형 목록을 작성하고 합계를 기준으로 정렬합니다 (내림차순).

먼저 "발 목록"에서 가장 값이 높은 사각형을 선택합니다. 그런 다음 이전에 발견 된 사각형과 교차하지 않는 차선책 사각형 4 개를 반복적으로 선택합니다.


단계적으로 진행하면 어떻게 될까요? 먼저 전역 최대 값을 찾고 필요한 경우 값이 주어진 주변 지점을 처리 한 다음 발견 된 영역을 0으로 설정하고 다음 영역에 대해 반복합니다.


이 질문에 대한 답은 확실하지 않지만 이웃이없는 n 개의 가장 높은 봉우리를 찾을 수있는 것 같습니다.

여기에 요점이 있습니다. Ruby에 있지만 아이디어는 명확해야합니다.

require 'pp'

NUM_PEAKS = 5
NEIGHBOR_DISTANCE = 1

data = [[1,2,3,4,5],
        [2,6,4,4,6],
        [3,6,7,4,3],
       ]

def tuples(matrix)
  tuples = []
  matrix.each_with_index { |row, ri|
    row.each_with_index { |value, ci|
      tuples << [value, ri, ci]
    }
  }
  tuples
end

def neighbor?(t1, t2, distance = 1)
  [1,2].each { |axis|
    return false if (t1[axis] - t2[axis]).abs > distance
  }
  true
end

# convert the matrix into a sorted list of tuples (value, row, col), highest peaks first
sorted = tuples(data).sort_by { |tuple| tuple.first }.reverse

# the list of peaks that don't have neighbors
non_neighboring_peaks = []

sorted.each { |candidate|
  # always take the highest peak
  if non_neighboring_peaks.empty?
    non_neighboring_peaks << candidate
    puts "took the first peak: #{candidate}"
  else
    # check that this candidate doesn't have any accepted neighbors
    is_ok = true
    non_neighboring_peaks.each { |accepted|
      if neighbor?(candidate, accepted, NEIGHBOR_DISTANCE)
        is_ok = false
        break
      end
    }
    if is_ok
      non_neighboring_peaks << candidate
      puts "took #{candidate}"
    else
      puts "denied #{candidate}"
    end
  end
}

pp non_neighboring_peaks

천문학 및 우주론 커뮤니티에서 사용할 수있는 여러 가지 광범위한 소프트웨어가 있습니다. 이것은 역사적으로나 현재까지 중요한 연구 분야입니다.

천문학자가 아니더라도 놀라지 마십시오. 일부는 야외에서 사용하기 쉽습니다. 예를 들어 astropy / photutils를 사용할 수 있습니다.

https://photutils.readthedocs.io/en/stable/detection.html#local-peak-detection

[여기에서 짧은 샘플 코드를 반복하는 것은 약간 무례한 것 같습니다.]

관심이있을 수있는 기술 / 패키지 / 링크의 불완전하고 약간 편향된 목록이 아래에 나와 있습니다. 주석에 더 추가하면 필요에 따라이 답변을 업데이트 할 것입니다. 물론 정확도와 컴퓨팅 리소스의 균형이 있습니다. [솔직히 이와 같은 단일 답변으로 코드 예제를 제공하기에는 너무 많아서이 답변이 날아갈 지 모르겠습니다.]

소스 추출기 https://www.astromatic.net/software/sextractor

MultiNest https://github.com/farhanferoz/MultiNest [+ pyMultiNest]

ASKAP / EMU 소스 찾기 도전 : https://arxiv.org/abs/1509.03931

Planck 및 / 또는 WMAP 소스 추출 문제를 검색 할 수도 있습니다.

...

참고 URL : https://stackoverflow.com/questions/3684484/peak-detection-in-a-2d-array

반응형