2Dアレイでのピーク検出


874

犬の足の下の圧力を測定する獣医クリニックを支援しています。データ分析にPythonを使用していますが、足を(解剖学的)サブ領域に分割しようとしています。

各足の2Dアレイを作成しました。これは、時間の経過とともに足によって読み込まれた各センサーの最大値で構成されます。Excelを使用して「検出」したい領域を描画する1つの足の例を次に示します。これらは、センサーの周囲にある2 x 2のボックスで、極大値を持ち、合計が最大になります。

代替テキスト

だから私はいくつかの実験を試みて、単純に各列と行の最大値を探すことに決めました(足の形状のために一方向に見ることはできません)。これは、別々の足指の位置をかなり「検出」するようですが、隣接するセンサーにもマークを付けます。

代替テキスト

それで、これらの最大値のうちのどれが私が欲しいものであるかをPythonに伝える最良の方法は何でしょうか?

注:2x2の正方形はつま先を分離する必要があるため、重ねることはできません。

また、便宜上2x2を採用しましたが、より高度なソリューションも歓迎しますが、私は人間の運動科学者なので、本物のプログラマでも数学者でもないので、「シンプル」にしてください。

ここだとロードすることができますバージョンは、np.loadtxt


結果

そこで、@ jexteeの解決策を試しました(以下の結果を参照)。ご覧のとおり、前足には非常に効果的ですが、後脚にはあまり効果がありません。

具体的には、4番目のつま先である小さなピークを認識できません。これは明らかに、ループがどこにあるかを考慮せずに、ループがトップダウンで最低値に向かっているという事実に固有です。

@jexteeのアルゴリズムを調整して、4番目の足指も見つけることができるようにする方法を知っている人はいますか?

代替テキスト

他のトライアルはまだ処理していないため、他のサンプルを提供することはできません。しかし、私が以前に提供したデータは、各足の平均でした。このファイルは、プレートと接触した順番で9足の最大データを含む配列です。

この画像は、それらがプレート上で空間的に広がっていることを示しています。

代替テキスト

更新:

私が興味のある人のためのブログを設定している私はすべての生の測定値とのセットアップのSkyDriveを持っています。つまり、より多くのデータを要求する人にとっては、より強力なものになります!


新しいアップデート:

足の検出足の並べ替えに関する質問に協力していただいた結果、すべての足の足指の検出を確認することができました。結局のところ、私自身の例のような足のサイズの足以外では、うまく機能しません。後から考えてみると、2x2を恣意的に選択するのは私自身の責任です。

これがうまくいかない良い例です:爪がつま先として認識され、「かかと」が非常に幅広で、2回認識されます!

代替テキスト

足が大きすぎるため、オーバーラップのない2x2サイズを使用すると、一部の足指が2回検出されます。逆に言えば、小型犬では5番目のつま先が見つからないことがよくあります。これは、2x2の領域が大きすぎるために発生していると考えられます。

私のすべての測定で現在の解決策を試した後、ほとんどすべての小型犬では5番目のつま先が見つからず、大型犬への影響の50%以上でさらに多くの発見があるという驚異的な結論に達しました。

だから私は明らかにそれを変更する必要があります。私の推測ではneighborhood、小型犬の場合はサイズを小さくし、大型犬の場合はサイズを大きくしています。しかしgenerate_binary_structure、配列のサイズを変更することはできません。

したがって、足のサイズにつま先の領域のスケールを設定するなど、つま先の位置を特定するためのより良い提案が他にあることを願っていますか?


コンマは値の区切り文字ではなく小数点以下の桁数であると思いますか?
MattH

はい、カンマです。そして、@ Christian、私はそれを読みやすいファイルに貼り付けようとしていますが、それでも失敗します:(
Ivo Flipse

3
私が実現可能性調査をしているので、何でも本当に行きます。したがって、私は、サブリージョンを含む、圧力を定義するための多くの方法を探しています。また、方向を推定するために、「親指」と「つま先」の側面を区別できる必要があります。しかし、これは以前に行われていないため、何が見つかるか分からない:-)
Ivo Flipse

2
@ロン:この研究の目標の1つは、システムが適切な犬のサイズ/体重を確認することです。そのため、この犬は約20 kgでした。かなり小さい(および大きい)ものもあり、実際の小さいものには同じことができないと思っています。
Ivo Flipse

2
@frank足は経時的に測定されるため、3番目の次元です。ただし、彼らはその場所から移動しないので(相対的に言えば)、2Dでつま先がどこにあるのかに主に関心があります。3Dアスペクトはその後無料で提供されます
Ivo Flipse

回答:


332

極大フィルターを使用してピークを検出しました。4つの足の最初のデータセットの結果は次のとおりです。 ピーク検出結果

私はまた、9つの足の2番目のデータセットで実行しましたが、同様に機能しました

方法は次のとおりです。

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、マスクを使用してすべての個別のオブジェクトにラベルを付けるだけです。その後、個別にプレイすることができます。

背景にはノイズが多くないため、この方法はうまく機能することに注意してください。もしそうなら、バックグラウンドで他の不要なピークの束を検出します。もう1つの重要な要素は、近隣の規模です。ピークサイズが変化した場合は、調整する必要があります(はほぼ比例したままです)。


1
(eroded_background ^ local_peaks)よりも簡単な解決策があります。ただやる(前景とローカルのピーク)
Ryan Soklaski 2017年

53

解決

データファイル: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ケースですべての足を評価できます。


結果リストが与えられた場合、彼は手で足をフィルターで取り除く必要があります...最上位の4つの結果を選択すると、最大値6.8を含む2x2正方形を構築する4つの可能性が得られます
Johannes Charra

2x2ボックスはオーバーラップできません。統計を行いたい場合、同じリージョンを使用したくないので、リージョンを比較したいからです:-)
Ivo Flipse

回答を編集しました。これで、結果に重複する正方形はありません。
サスタニン

1
私はそれを試しました、そして、それは前足のために働くようです、しかし後ろの足のためにはそれほどではありません。どこを見ればわかるか試してみる必要があると思います
Ivo Flipse

1
疑似コードで指を検出する方法を説明しました。よろしければ明日の夕方に実装してみます。
サスタニン

34

これは画像登録の問題です。一般的な戦略は次のとおりです。

  • データに関する既知の例、または何らかの事前知識がある
  • データを例に合わせるか、例をデータに合わせます。
  • そもそもデータがおおよそ揃っている場合に役立ちます。

大まかな準備ができたアプローチは次のとおりです。「機能する可能性のある最も愚かなこと」:

  • おおよその場所で、5つのつま先の座標から始めます。
  • それぞれを繰り返して、丘の頂上に登ります。つまり、現在の位置を指定し、その値が現在のピクセルより大きい場合は、最大の隣接ピクセルに移動します。つま先の座標が動かなくなったら停止します。

方向の問題に対処するために、基本的な方向(北、北東など)の初期設定を8個程度にすることができます。それぞれを個別に実行し、2つ以上の足指が同じピクセルに到達する結果を破棄します。これについてはもう少し考えますが、この種のことはまだ画像処理で研究されています-正解はありません!

少し複雑なアイデア:(加重)K平均クラスタリング。それは悪いことではありません。

  • 5つのつま先の座標から始めますが、これらは「クラスターの中心」です。

次に、収束するまで繰り返します。

  • 各ピクセルを最も近いクラスターに割り当てます(各クラスターのリストを作成するだけです)。
  • 各クラスターの重心を計算します。各クラスターについて、これは、Sum(座標*輝度値)/ Sum(座標)です。
  • 各クラスターを新しい重心に移動します。

この方法はほぼ間違いなくはるかに優れた結果をもたらし、つま先の特定に役立つ可能性がある各クラスターの質量を取得します。

(ここでも、クラスタの数を事前に指定しています。クラスタリングでは、密度を何らかの方法で指定する必要があります。この場合は適切なクラスタの数を選択するか、クラスタの半径を選択して、終了する数を確認します後者の例は、平均シフトです。)

実装の詳細やその他の詳細の欠如について申し訳ありません。私はこれをコーディングしますが、期限があります。来週までに他に何も機能しなかった場合はお知らせください。試してみます。


1
問題は、足の向きが変わってしまい、最初から正しい足のキャリブレーション/ベースラインがないことです。さらに、多くの画像認識アルゴリズムが私のリーグから少し外れていることを恐れています。
Ivo Flipse

「大まかな準備ができた」アプローチは非常に単純です-多分私は考えをよくしませんでした。説明のためにいくつかの疑似コードを入れます。
CakeMaster

私はあなたの提案が後肢の認識を修正するのに役立つと感じています、私は「方法」がわからないだけです
Ivo Flipse

別のアイデアを追加しました。ちなみに、大量の適切なデータがある場合は、どこかでオンラインにしておくと便利です。それは画像処理/機械学習を研究している人々にとって有用かもしれません、そしてあなたはそれからいくつかのより多くのコードを得るかもしれません...
CakeMaster

1
データ処理を単純なWordpressブログに書き留めて、単に他の人のために使用することを考えていました。とにかくそれを書き留める必要があります。私はあなたの提案をすべて気に入っていますが、締め切りのない誰かを待たなければならないのではないかと心配しています;-)
Ivo Flipse

18

永続的ホモロジーを使用してデータセットを分析すると、次の結果が得られます(クリックして拡大):

結果

これは、この回答で説明されているピーク検出方法の2Dバージョンです。上の図は、永続性でソートされた0次元の永続ホモロジークラスを示しています。

scipy.misc.imresize()を使用して、元のデータセットを2倍に拡大しました。ただし、4つの足を1つのデータセットと見なしたことに注意してください。それを4つに分割すると、問題が簡単になります。

方法論。 この非常に単純な背景にある考え方:各ピクセルにレベルを割り当てる関数の関数グラフを考えてみましょう。次のようになります。

3D関数グラフ

ここで、高さ255の水位を考えます。この水位は継続的に下降します。地元のマキシマ島でポップアップ(誕生)。サドルポイントでは、2つの島が合流します。低い方の島が高い方の島(死)にマージされると考えます。いわゆる永続性図(0次元のホモロジークラス、私たちの島の)は、すべての島の死後出生値を示しています。

持続性図

島の存続は、誕生と死のレベルの違いです。灰色の主対角線までのドットの垂直距離。この図は、持続性を減らすことで島にラベルを付けています。

最初の写真は、島の誕生の場所を示しています。この方法は、極大値を与えるだけでなく、前述の持続性によってそれらの「有意性」を定量化します。次に、永続性が低すぎるすべての島を除外します。ただし、この例では、すべての島(つまり、すべての極大値)が、探しているピークです。

Pythonコードはここにあります


16

この問題は物理学者によってある程度深く研究されてきました。ROOTには適切な実装があります。見てTSpectrumのクラス(特にTSpectrum2お使いの場合)と、それらのドキュメント。

参照:

  1. M.Morhac et al .:多次元同時ガンマ線スペクトルのバックグラウンド除去法。物理学研究における核装置と方法A 401(1997)113-132。
  2. M.Morhac et al .:効率的な1次元および2次元のゴールドデコンボリューションとそのガンマ線スペクトル分解への応用。Nuclear Instruments and Methods in Physics Research A 401(1997)385-408。
  3. M.Morhac et al .:多次元同時ガンマ線スペクトルにおけるピークの同定。Nuclear Instruments and Methods in Research Physics A 443(2000)、108-125。

...そしてNIMのサブスクリプションにアクセスできない人のために:


記事を一目で見れば、ここで私が試みているのと同じデータ処理を説明しているように見えますが、私のプログラミングスキルを大幅に上回っていると思います:(
Ivo Flipse

@Ivo:自分で実装しようとしたことがありません。ROOTを使用します。それでも、Pythonバインディングはありますが、ROOTはかなり重いパッケージであることに注意してください。
dmckee ---元モデレーターの子猫2010

@Ivo Flipse:dmckeeに同意します。あなたは他の答えで多くの有望なリードを持っています。それらすべてが失敗し、しばらく時間を費やしたい場合は、ROOTを掘り下げることができ、(おそらく)必要なことを実行します。(自然なC ++ではなく)Pythonバインディングを介してROOTを学習しようとした人は誰も知らなかったので、幸運を祈ります。
physicsmichael 2010

13

ここにアイデアがあります:画像の(離散)ラプラシアンを計算します。元の画像よりも劇的な方法で、それが(負で)最大値で大きくなることを期待します。したがって、マキシマは見つけやすくなります。

ここに別のアイデアがあります。高圧スポットの一般的なサイズがわかっている場合は、最初に同じサイズのガウスで画像を畳み込むことによって画像を平滑化できます。これにより、処理する画像が簡単になる場合があります。


11

私の頭の上のいくつかのアイデア:

  • スキャンの勾配(導関数)を取得し、それが誤った呼び出しを排除するかどうかを確認します
  • 極大値の最大値を取る

また、OpenCVを確認することもできます。これにはかなりまともなPython APIがあり、役立つと思われる関数がいくつかある場合があります。


勾配を使用すると、勾配の急勾配を計算する必要があります。これが特定の値を超えると、「ピーク」があることがわかりますか?私はこれを試しました、しかし、いくつかのつま先は他のいくつか(8 N / cm)と比較して非常に低いピーク(1.2 N / cm)しかありません。では、非常に低い勾配でピークをどのように処理すればよいですか?
Ivo Flipse

2
勾配を直接使用できない場合、過去に私のために働いていたのは、勾配と最大値を調べることでした。たとえば、勾配が局所極値であり、局所最大値である場合、私は興味。
ChrisC

11

これで先に進むには十分だと思いますが、k-meansクラスタリング手法の使用を提案せざるを得ません。k-meansは教師なしのクラスタリングアルゴリズムであり、データ(任意の数の次元で-私はたまたまこれを3Dで行います)を取得し、異なる境界を持つk個のクラスターに配置します。あなたはこれらの犬が(すべき)足の指の数を正確に知っているので、ここでいいです。

さらに、これはScipyに実装されており、非常に優れています(http://docs.scipy.org/doc/scipy/reference/cluster.vq.html)。

3Dクラスターを空間的に解決するためにできることの例を次に示します。 ここに画像の説明を入力してください

やりたいことは少し異なりますが(2Dと圧力の値が含まれます)、まだ試してみることができると思います。


10

生データをありがとう。私は電車に乗っており、これは私が得た限りです(私の停車駅が近づいています)。私はあなたのtxtファイルを正規表現でマッサージし、視覚化のためにいくつかのJavaScriptを含むhtmlページにそれを展開しました。私と同じように、Pythonよりもハッキングされやすいと思う人もいるので、ここで共有します。

良いアプローチは、スケールと回転が不変であることだと思います。次のステップは、ガウシアンの混合を調査することです。(各足パッドはガウシアンの中心です)。

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

代替テキスト


1
これは、誰かがPythonで証明できる場合にのみ、推奨されるガウステクニックが機能するという概念の証明だと思います;-)
Ivo Flipse

8

物理学者の解決策:
位置によって識別される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_ialfa実験的にピークされるパラメータです)

メトロポリスヘイスティングスの魔法を実行する時間です
。1.ランダムマーカーを選択し、ランダムな方向に1ピクセル移動します。
2.この動きが引き起こしたエネルギーの差であるdEを計算します。
3. 0〜1の一様な乱数を取得し、rと呼びます。
4. dE<0またはの場合exp(-beta*dE)>r、移動を受け入れて1に移動します。そうでない場合は、移動を取り消して1に進みます。
これは、マーカーが足に収束するまで繰り返す必要があります。ベータはスキャンを制御してトレードオフを最適化するため、実験的にも最適化する必要があります。また、シミュレーション(シミュレーテッドアニーリング)の時間とともに常に増加させることもできます。


これが私の例でどのように機能するかを示すことに注意してください?私は本当に高レベルの数学が好きではないので、あなたが提案した式を解明するのにすでに苦労しています:(
Ivo Flipse

1
これは高校の数学です。おそらく私の表記は単に難読化されています。確認する予定があるのでお楽しみに。
mbq

4
私は素粒子物理学者です。長い間、私たちの専門分野の頼りになるソフトウェアツールはPAWと呼ばれ、「マーカー」と呼ばれるグラフに関連するエンティティがありました。最初の数回、この回答がどれほど混乱していたか想像できます...
dmckee --- ex-moderator kitten

6

大型望遠鏡で同様のことをするときに使用した別のアプローチを次に示します。

1)最も高いピクセルを検索します。それができたら、その周りを検索して2x2の最適なフィットを探すか(2x2の合計を最大化する可能性があります)、または最も高いピクセルを中心とした4x4のサブ領域内で2dガウスフィットを実行します。

次に、見つけた2x2ピクセルをピークの中心の周りでゼロ(またはおそらく3x3)に設定します

1)に戻り、最も高いピークがノイズしきい値を下回るまで繰り返すか、必要なすべてのつま先を手に入れます


これを行うコード例を共有したいですか?私はあなたがやろうとしていることを追跡できますが、自分でそれをコード化する方法がわかりません
Ivo Flipse

私は実際にはMatlabと一緒に仕事をしているので、はい、それはすでに役に立ちます。しかし、本当に外部の関数を使用する場合、Pythonでそれを複製するのは難しいかもしれません
Ivo Flipse

6

トレーニングデータを作成できる場合は、おそらくニューラルネットワークを試してみる価値があります...しかし、これには手作業で注釈を付けた多くのサンプルが必要です。


トラブルに値するものであれば、手作業で大きなサンプルに注釈を付けてもかまいません。私の問題は、次のようになります。私は、ニューラルネットワークをプログラミングについて何も知らないので、私は、これを実装するのですか
イヴォFlipse

6

大まかな概要...

おそらく、接続されたコンポーネントのアルゴリズムを使用して、各足の領域を分離する必要があります。wikiには、これについて(いくつかのコードを使用して)適切な説明があります。http//en.wikipedia.org/wiki/Connected_Component_Labeling

4つまたは8つの接続を使用するかどうかを決定する必要があります。個人的には、ほとんどの問題で私は6連結性を好みます。とにかく、各「足跡」を接続された領域として分離したら、その領域を反復して最大値を見つけるのは簡単です。最大値を見つけたら、所定の「つま先」として特定するために、所定のしきい値に達するまで繰り返し領域を拡大できます。

ここでの微妙な問題の1つは、コンピュータービジョンテクニックを使用して何かを右/左/前/後足として識別し始め、個々の足指を調べ始めるとすぐに、回転、傾斜、および平行移動を考慮に入れなければならないということです。これは、いわゆる「モーメント」の分析を通じて達成されます。ビジョンアプリケーションで考慮すべきいくつかの異なる瞬間があります。

中心モーメント:並進不変正規化モーメント:スケーリングと並進不変huモーメント:並進、スケール、回転不変

モーメントの詳細については、wikiで「イメージモーメント」を検索してください。



4

jetxeeのアルゴリズムを使用して、少しごまかすことができるようです。彼は最初の3本のつま先を正常に見つけており、4本目の足がどこに基づいているかを推測できるはずです。


4

興味深い問題。私が試す解決策は次のとおりです。

  1. 2Dガウスマスクを使用したたたみ込みなどのローパスフィルターを適用します。これにより、(おそらく浮動小数点ではないが)一連の値が得られます。

  2. 各足パッド(または足指)の既知のおおよその半径を使用して、2D非最大抑制を実行します。

これにより、近接している複数の候補者がいない状態で最大のポジションが得られます。明確にするために、ステップ1のマスクの半径も、ステップ2で使用した半径と同じである必要があります。この半径は選択可能であるか、獣医が事前に明示的に測定することができます(年齢、品種などによって異なります)。

提案されている解決策のいくつか(平均シフト、ニューラルネットなど)はおそらくある程度は機能しますが、過度に複雑で、おそらく理想的ではありません。


私はたたみ込み行列とガウスフィルターの経験がありません。そのため、私の例でどのように機能するかを示しますか?
Ivo Flipse

3

さて、ここにいくつかの単純でそれほど効率的ではないコードがありますが、このサイズのデータ​​セットの場合は問題ありません。

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正方形をすべて削除します。

最後の足(最初の写真の右端にある合計が最小のもの)を除いて、正常に動作しているようですが、合計が大きい2つの2x2の正方形が2つあります(合計はお互い)。そのうちの1つは2x2の正方形から1つの正方形を選択していますが、もう1つは左側にあります。幸いなことに、運が良ければより多くのものが選択されるようになりますが、これには、常に実際に欲しいものを取得するために他のアイデアを使用する必要がある場合があります。


あなたの結果は@Jexteeの答えのものと同じだと思います。または、少なくとも私はそれをテストしているようです。
Ivo Flipse


1

たぶんここでは単純なアプローチで十分です:平面上のすべての2x2正方形のリストを作成し、それらを合計(降順)で並べます。

最初に、最も高い値の正方形を「足リスト」に選択します。次に、以前に見つかったどの正方形とも交差しない次善の正方形を4つ繰り返し選択します。


私は実際にすべての2x2の合計でリストを作成しましたが、それらを注文したとき、それらを繰り返し比較する方法がわかりませんでした。私の問題は、それを並べ替えると、座標がわからなくなってしまうことでした。おそらく、座標をキーとして、それらを辞書に貼り付けることができます。
Ivo Flipse

はい、何らかの辞書が必要になります。あなたのグリッドの表現は、ある種の辞書であると私は思いました。
Johannes Charra

さて、上記の画像は乱雑な配列です。残りは現在、多次元リストに格納されています。辞書の反復にはあまり慣れていませんが、おそらくそれをやめた方がいいでしょう
Ivo Flipse

1

天文学と宇宙論のコミュニティから入手できるソフトウェアには、いくつかの広範なものがあります。これは、歴史的にも現在も重要な研究分野です。

あなたが天文学者でなくても心配しないでください-いくつかは野外で使いやすいです。たとえば、astropy / photutilsを使用できます。

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

[ここで短いサンプルコードを繰り返すのは少し失礼なようです。]

興味深いかもしれないテクニック/パッケージ/リンクの不完全で少し偏ったリストを以下に示します-コメントにさらに追加してください。必要に応じてこの回答を更新します。もちろん、精度と計算リソースのトレードオフがあります。[正直なところ、このような1つの回答でコード例を示すには多すぎるため、この回答が飛ぶかどうかはわかりません。]

Source Extractor https://www.astromatic.net/software/sextractor

マルチネストhttps://github.com/farhanferoz/MultiNest [+ pyMultiNest]

ASKAP / EMUソース検索チャレンジ:https : //arxiv.org/abs/1509.03931

PlanckやWMAPのソース抽出の課題を検索することもできます。

...


0

ステップバイステップの場合:最初に全体の最大値を見つけ、必要に応じて値を指定して周囲のポイントを処理し、次に見つかった領域をゼロに設定して、次の領域について繰り返します。


うーん、ゼロに設定すると、少なくともそれがそれ以降の計算から削除されるため、便利です。
Ivo Flipse 2010

ゼロに設定する代わりに、手で選んだパラメータでガウス関数を計算し、見つかった値を元の圧力測定値から差し引くことができます。つまり、つま先がセンサーを押している場合、最も高い押し付けポイントを見つけることにより、そのつま先がセンサーに及ぼす影響を減らし、圧力値が高い隣接セルを排除します。 en.wikipedia.org/wiki/File:Gaussian_2d.png
Daniyar

私のサンプルデータ@Daniyarに基づいて例を示しますか?私はそのような種類のデータ処理に本当に慣れていないので
Ivo

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

試してみて、Pythonコードに変換できるかどうかを確認します:-)
Ivo Flipse

妥当な長さであれば、要旨にリンクするのではなく、コードを投稿自体に含めてください。
agf
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.