767

私は犬の足の下の圧力を測定する獣医クリニックを手伝っています。私はデータ分析にPythonを使用していますが、今度は足を(解剖学的な)小領域に分割しようとしています。

各足の2D配列を作成しました。これは、足によって時間の経過とともにロードされた各センサーの最大値で構成されています。これは私が '検出'したい領域を描くためにExcelを使った一足の例です。これらは、極大値を持つセンサーの周囲2×2の箱で、合計で最大の合計を持ちます。

alt text

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

alt text

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

注意:2x2の正方形は別々のつま先でなければならないので、重なり合うことはできません。

また、私は便利なものとして2x2を取り、より高度な解決策は大歓迎ですが、私は単に人間の運動科学者なので、私は本当のプログラマーでも数学者でもないので、それを「単純」にしてください。

これがロードできるバージョンnp.loadtxt


結果

そこで、私は@ jexteeの解決策を試しました(以下の結果を参照)。お分かりのように、それは前足には非常に効果的ですが、後肢にはあまり効果的ではありません。

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

4番目のつま先も見つけられるように、@ jexteeのアルゴリズムを調整する方法を知っている人はいますか?

alt text

まだ他の試験を処理していないので、他のサンプルを提供することはできません。しかし、私が以前に与えたデータは各足の平均でした。このファイルは、それらがプレートと接触した順序で9本の足の最大データを持つ配列です。

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

alt text

更新:

興味のある人のためにブログを立ち上げましたそして私はすべての生の測定値を使ってSkyDriveをセットアップしました。それで、より多くのデータを要求する人には:あなたにもっと力を!


新しいアップデート:

だから私は私の質問で私が得た助けの後に足の検出そして足のソート、私はついに各足のつま先の検出をチェックすることができました!結局のところ、それは私の自身の例のもののような大きさの足以外の何にもそれほどうまくいかない。後知恵ではもちろん、それは2×2をとても恣意的に選ぶことに対する私自身の責任です。

これは、うまくいかない場合の良い例です。ネイルがつま先として認識され、「ヒール」が非常に広いため、2回認識されます。

alt text

足が大きすぎるので、重ならないように2 x 2のサイズを取ると、つま先が2回検出されます。反対に、小さな犬では5番目のつま先が見つからないことがよくありますが、これは2×2の面積が大きすぎることが原因と考えられます。

後に私のすべての測定に対して現在の解決策を試す私はほとんどすべての私の小型犬のためにそれが5番目のつま先を見つけられず、大型犬のための影響の50%以上でそれがもっと見つけるだろうという驚異的な結論に達しました!

だから明らかに私はそれを変更する必要があります。私自身の推測は、neighborhood小さい犬のための小さいものと大きい犬のための大きいものへ。しかしgenerate_binary_structure私は配列のサイズを変更させないだろう。

したがって、足のサイズに合わせて足の面積を調整するなど、他の誰かが足の位置を特定するためのより良い提案を得られることを願っていますか?


  • これはそれほど簡単ではありませんが、面白いようです。 pythonで読みやすい形式でもっと多くのデータセット(足)を提供できますか?だから私はいくつかのアイデアを試すことができました。 - Christian
  • これは、私が長い間読んだ中で最も楽しい質問の1つになっています。イラストも付いています。私の帽子、君に敬意を表します。 - wheaties
  • 実現可能性調査を行っているうちに、何でも結構です。そのため、サブリージョンも含め、プレッシャーを定義する方法はいくつもあります。また、つま先の親指を区別できるようにする必要があります。と、つま先の小さなつま先'方向を推定するためにしかし、これまでに行われたことがないので、何が見つかったのかわかりません:-) - Ivo Flipse
  • @イボ:私たちは、「どうやって印刷するのですか。こんにちは、世界」です。 VBで」これはかなり新鮮な空気の息だと疑問に思う。 - Pavel Minaev
  • ここには明らかに一つ以上の答えがあるので、私は賞金を分割することができればと願っています。しかし、その能力が欠如しているため、私は最高得票数の回答を得ます。 - Pavel Minaev

21 답변


270

を使用してピークを検出しました極大値フィルタ。これが4足の最初のデータセットの結果です。Peaks detection result

私はまた、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見栄えの良い結果。私が最初に考えたのは、次のように領域最大値検出アルゴリズムを使用することでした。regmaxからピモルフ。 - gnovice
  • これは非常に有望に思えます、私はより多くの測定値を解析しようとするでしょう、それで私たちはそれが我慢できるかどうかを見ることができます:-) - Ivo Flipse
  • ドキュメントの説明から@gnovice、local_max行と同じことをすることを理解しています。 - Ivan
  • あなたは侵食された背景のイメージを見せることができますか? ' erode'の意味を理解しようとしています。 - lennon310
  • (eroded_background ^ local_peaks)よりも簡単な解決策があります。してください(前景とローカルピーク) - Ryan Soklaski

42

溶液

データファイル: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 x 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 x 2の正方形上でまったく同じ合計になります。

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

あなたが2x2の正方形以上の合計を持っているとき、あなたは使うことができますmax最大値を見つけるsortまたはsortedピークを見つけるために。

ピークの位置を覚えておくために、すべての値(合計)をその平らな配列の平らな配列に結合します(を参照)。zip)それから結果を印刷するときに、もう一度行/列の位置を計算します。

ノート

2×2の正方形を重ね合わせることができました。編集バージョンでは、重ならない四角形だけが結果に表示されるように、それらの一部が除外されています。

指を選ぶ(アイデア)

もう1つの問題は、すべてのピークからどのようにして指になる可能性があるかを選択する方法です。私はうまくいくかどうかわからない考えを持っています。今は実装する時間がないので、疑似コードだけです。

前部の指がほぼ完全な円の上にある場合、後部の指はその円の内側にあるはずです。また、前部フィンガは、多かれ少なかれ等間隔である。これらのヒューリスティックな性質を使って指を検出しようとするかもしれません。

擬似コード

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通りの方法があります。


  • 結果リストが表示されたら、足を手動で除外する必要があります。一番上の4つの結果を選択すると、最大値6.8を含む2 x 2の四角形を作成する4つの可能性があります。 - Johannes Charra
  • 2×2のボックスは重ならないようにします。統計を行いたいのであれば、同じ地域を使いたくないので、地域を比較したいのですが:-) - Ivo Flipse
  • あなたがそれを必要とするならば、私は重ならない領域だけをフィルターするためにコードスニペットを書くかもしれません。少しあと。 - sastanin
  • 私はそれを試してみました、そしてそれは前足のために働くように見えます、しかし後肢のためにそれほどではありません。どこを見ればいいか知っているものを試してみる必要があると思います - Ivo Flipse
  • 擬似コードで指を検出する方法を説明しました。あなたがそれを好めば、私は明日の夜それを実行しようとするかもしれません。 - sastanin

27

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

  • 既知の例、またはある種の前のデータに。
  • データを例に合わせるか、例をデータに合わせます。
  • あなたのデータがだいたいそもそも並んでいる。

これは大まかで手軽な方法です。、「おそらくうまくいく可能性のある最も厄介なこと」

  • おおよそあなたが期待するところで5本のつま先座標から始めてください。
  • 一人一人で、丘の上に繰り返し登ります。すなわち、現在の位置が与えられた場合、その値が現在の画素よりも大きい場合、最大隣接画素に移動する。つま先の座標が移動しなくなったら停止します。

オリエンテーションの問題に対処するために、基本的な方向(北、北東など)に8程度の初期設定を設定できます。それぞれを個別に実行し、2つ以上のつま先が同じピクセルになるような結果はすべて捨てます。もう少し考えてみますが、このようなことはまだ画像処理で研究されています - 正しい答えはありません!

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

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

収束するまで繰り返します。

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

この方法はほぼ確実にはるかに良い結果を与えます、そしてあなたはつま先を識別するのを助けるかもしれない各クラスタの質量を得ます。

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

実装の詳細やその他の詳細が欠けていることについて申し訳ありません。これをコーディングしますが、期限があります。来週までに他に何も上手くいかなかったら私に知らせてくださいそして私はそれに打撃を与えるつもりです。


  • 問題は、足の向きが変わるため、最初に正しい足のキャリブレーション/ベースラインがないことです。さらに、私は多くの画像認識アルゴリズムが私のリーグから少し外れていることを恐れています。 - Ivo Flipse
  • 「大まかで準備完了」アプローチは非常に簡単です - おそらく私はその考えをよく考えていませんでした。説明のために、擬似コードを入れます。 - CakeMaster
  • 私はあなたの提案が後足の認識を直すのに役立つと感じています、私はどうしたらいいのかわからない' - Ivo Flipse
  • 別のアイデアを追加しました。ところで、良いデータが大量にある場合は、オンラインでどこかに置いておくのがかっこいいでしょう。それは画像処理/機械学習を勉強している人々にとって役に立つかもしれません、そしてあなたはそれからもう少しコードを得るかもしれません... - CakeMaster
  • 私は自分のデータ処理を単純なWordpressのブログに書き留めようと考えていたのですが、それは他の人に役立つものであり、とにかく書き留めなければなりません。私はあなたの提案をすべて気に入っていますが、期限のない誰かを待たなければならないのではないかと思います;-) - Ivo Flipse

12

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

参考文献:

  1. M.Morhac et al。:多次元コインシデンスガンマ線スペクトルのバックグラウンド除去法Nuclear Instruments and Methods in Physics Research A 401(1997)113-132。
  2. M.Morhac et al .:効率的な一次元および二次元のゴールド逆畳み込みおよびそのガンマ線スペクトル分解への応用。 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
  • @イボ:自分で実装しようとしたことは一度もありません。私はただROOTを使います。 Pythonバインディングもありますが、ROOTはかなり重いパッケージです。 - dmckee
  • @ Ivo Flipse:私はdmckeeに同意します。あなたは他の答えにおいて多くの有望な見込み客を持っています。それらがすべて失敗して、しばらく投資する気がしたら、ROOTを詳しく調べれば(おそらく)必要なことができるでしょう。私はPythonのバインディングを介してROOTを学ぼうとした人(これは天然のC ++ではなく)を知らなかったので、幸運を祈っています。 - physicsmichael

10

これがアイデアです:あなたは画像の(離散的な)ラプラシアンを計算します。私はそれが元のイメージより劇的である方法で、最大で(マイナスで)大きくなると予想するでしょう。したがって、最大値を見つけやすくなります。

もう一つのアイデアがあります:あなたが高圧スポットの典型的なサイズを知っているならば、あなたは最初にそれを同じサイズのガウスで畳み込むことによってあなたのイメージを滑らかにすることができます。これにより、処理する画像が簡単になります。


10

永続的相同性を使用してデータセットを分析すると、次のようになります(クリックすると拡大します)。

Result

これは、この記事で説明されているピーク検出方法の2Dバージョンです。そう答え。上の図は、永続性によってソートされた0次元永続相同性クラスを単に示しています。

scipy.misc.imresize()を使用して、元のデータセットを2倍にアップスケールしました。ただし、4つの足は1つのデータセットと見なしました。それを4つに分割すると問題が簡単になります。

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

3D function graph

今度は、継続的により低いレベルまで下がる、高さ255の水位を考えてみましょう。極大島ではポップアップ(出生)。鞍点で2つの島が合流します。下の島は上の島に併合されると考えられます(死)。いわゆる永続図(0次元の相同性クラス、私たちの島)は、すべての島の出生時死亡数を表しています。

Persistence diagram

固執島のそれは出生レベルと死レベルの違いです。グレーの主対角線までのドットの垂直距離。この図は、残像を減らすことで島にラベルを付けます。

一番最初の写真は島の出生地を示しています。この方法は、極大値を与えるだけでなく、上記の持続性によってそれらの「重要性」を定量化します。その場合、持続性が低すぎると、すべての島が除外されます。しかし、あなたの例では、すべての島(つまり、すべての極大値)があなたが探しているピークです。

Pythonコードが見つかりますここに


9

頭の中からいくつかのアイデアをいくつか紹介します。

  • スキャンの勾配(微分)を取り、それが誤った呼び出しを排除するかどうかを確かめる
  • 極大値の最大値をとる

あなたも見てみたいかもしれませんOpenCVそれはかなりまともなPython APIを持っていて、あなたが役に立つと思うであろういくつかの機能を持っているかもしれません。


  • グラデーションを使用すると、斜面の勾配を計算する必要があることがわかります。これが特定の値を超えると、ピークになります。私はこれを試したが、つま先のいくつかは他のいくつか(8 N / cm)と比較して非常に低いピーク(1.2 N / cm)しかない。それでは、どのように私は非常に低い勾配でピークを扱うべきですか? - Ivo Flipse
  • グラデーションを直接使用できなかった場合、過去に私にとって効果的だったのは、グラデーションと最大値を調べることでした。勾配が極値で、極大値でI mの場合、関心点でI mとなります。 - ChrisC

7

生データをありがとう。私は電車に乗っています、そしてこれは私が得た限りです(私の停車場は上がっています)。私はあなたの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>

alt text


  • 私は、これが推奨されるGaussianテクニックがうまくいく可能性があるという概念実証であると考えています。ただし、誰かだけがPythonでそれを証明できれば今は;-) - Ivo Flipse

7

私はあなたがもう十分に行くことができると確信しています、しかし私は助けることができませんk平均クラスタリング方法を使うことを提案します。 k-meansは教師なしのクラスタリングアルゴリズムで、データを(任意の次元で - 私はこれを3Dで行います)受け取り、それを明確な境界を持つk個のクラスタに配置します。あなたがこれらの犬歯が持っているべきである(正確に)つま先を正確に知っているので、それはここで素晴らしいです。

さらに、それはScipyで実装されていて、本当に素晴らしいです(http://docs.scipy.org/doc/scipy/reference/cluster.vq.html

3Dクラスタを空間的に解決するために何ができるかの例を示します。enter image description here

あなたがやりたいことは少し異なります(2Dと圧力値を含みます)、しかし私はまだあなたがそれに打撃を与えることができると思います。


6

物理学者の解決策:

位置によって識別される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)2×2平方あたりの平均力X_ialfa実験的にピークになるパラメータです。

今度はMetropolis-Hastingsの魔法をやる時間です。

1.ランダムマーカーを選択し、ランダムな方向に1ピクセル移動します。

2.この移動が引き起こしたエネルギーの差であるdEを計算します。

3. 0から1までの一様乱数を得て、それをrと呼ぶ。

4.もしdE<0またはexp(-beta*dE)>r移動を受け入れて1に進む。そうでない場合は、移動を元に戻して1に進みます。

マーカーが足に収束するまでこれを繰り返します。 Betaはスキャンを最適化するトレードオフを制御するので、実験的にも最適化されるべきです。それはまたシミュレーション(シミュレーテッドアニーリング)の時間と共に絶えず増加することができる。


  • これが私の例でどのように機能するかを示すように気を付けてください。私は実際には高水準の数学には慣れていないので、あなたが提案した式を解明するのはすでに苦労しています:( - Ivo Flipse
  • これは高校の数学です、おそらく私の記法はちょうど難読化されています。私はそれをチェックする計画があるので、しばらくお待ちください。 - mbq
  • 私は素粒子物理学者です。長い間、私たちの学問分野のゴートゥーソフトウェアツールはPAWと呼ばれていました、そしてそれは「マーカー」と呼ばれるグラフに関連した実体を持っていました。私がこの答えを見つけた最初の2、3回の間にどれほど混乱したか想像することができます... - dmckee

5

大きな望遠鏡で似たようなことをするときに私が使った別のアプローチがあります。

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

それからあなたが見つけたそれらの2x2ピクセルをピーク中心の周りにゼロ(あるいは多分3x3)に設定してください

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


  • これを行うコード例を共有するように注意しますか。私はあなたがやろうとしていることに従うことができますが、自分自身でそれをコーディングする方法がわかりません - Ivo Flipse
  • 私はいくつかのMatlabのコードを持っている、それは役立ちますか? - Paulus
  • 私は実際にはMatlabと仕事をすることから来ているので、はい、それはすでに助けになるでしょう。しかし、あなたが本当に外部の関数を使うのであれば、私がPythonでそれを再現するのは難しいかもしれません。 - Ivo Flipse

5

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


  • 問題がある場合は、大きなサンプルに手作業で注釈を付けても構いません。私の問題は、ニューラルネットワークのプログラミングについて何も知らないので、どうやってこれを実装するのかということです。 - Ivo Flipse

5

大まかな概要...

あなたはおそらく各足領域を分離するためにコネクテッドコンポーネントアルゴリズムを使用したいと思うでしょう。 wikiはこれについての適切な説明を(いくつかのコードと共に)ここに持っています:http://en.wikipedia.org/wiki/Connected_Component_Labeling

4つか8つの関連性を使用するかどうかについて決定する必要があります。個人的には、ほとんどの問題で私は6連結性を好む。とにかく、各「足跡」をつながった領域として分離したら、その領域を繰り返し処理して最大値を見つけるのは簡単なはずです。最大値を見つけたら、それを特定の「つま先」として識別するために、あらかじめ決められたしきい値に達するまで領域を繰り返し拡大することができます。

ここで微妙な問題は、コンピュータビジョン技術を使用して何かを右/左/前/後足として識別し始め、個々のつま先を見始めるとすぐに、回転、スキュー、および並進を考慮に入れなければならないことです。これはいわゆる「モーメント」の分析によって達成されます。ビジョンアプリケーションで考慮すべきいくつかの異なる瞬間があります。

中心的な瞬間:翻訳不変 正規化モーメント:スケーリングと並進不変量 モーモーメント:並進、スケール、回転不変

モーメントに関するより多くの情報は、ウィキで "image moment"を検索することで見つけることができます。


4

おそらくあなたはGaussian Mixture Modelsのようなものを使うことができます。これはGMMをするためのPythonパッケージです(ちょうどグーグル検索をしました)。http://www.ar.media.kyoto-u.ac.jp/members/david/softwares/em/


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]

基本的には、左上の位置と2 x 2の各正方形の合計を使って配列を作成し、その合計で並べ替えます。それから私は最大の合計を持つ2x2の正方形を競合から外し、それをbest配列を作成し、この部分を使用していた他のすべての2 x 2の正方形を削除します。

最後の足(あなたの最初の写真の一番右にある合計が最も小さいもの)を除いてはうまくいくようですが、より大きな合計を持つ2つの2x2平方の適格な正方形があることがわかります。お互い)。そのうちの1つはまだあなたの2x2の正方形から1つの正方形を選択していますが、もう一方は左側にありません。幸いなことに、幸運にも私たちはあなたが望むものをもっと選択しているように見えますが、これはあなたがいつも欲しいものを得るために他のアイデアを使う必要があるかもしれません。


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

3

それはあなたがjetxeeのアルゴリズムを使って少しカンニングすることができるようです。彼は最初の3本のつま先を問題なく見つけています、そしてあなたは4本目がそれに基づいているところを推測できるはずです。


3

面白い問題です。私がしようとする解決策は以下の通りです。

  1. 2Dガウスマスクとの畳み込みなどのローパスフィルタを適用します。これはあなたに(おそらく、必ずしも浮動小数点ではない)値の束を与えるでしょう。

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

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

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


  • 畳み込み行列とガウシアンフィルタの使用経験がありません。では、この例でどのように機能するかを教えてください。 - Ivo Flipse

2

pythonを使って画像の中で極大値を見つけるための素晴らしいオプションがあることを皆さんに伝えたいのです。

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


0

たぶんここで素朴なアプローチで十分です:あなたの平面上のすべての2x2の正方形のリストを作り、それらを合計して(降順で)順序付けます。

まず、あなたの "足のリスト"に最も高い値の正方形を選択してください。次に、以前に見つかった正方形のいずれとも交差しない次善の正方形のうちの4つを繰り返し選択します。


  • 私は実際にすべての2x2の合計をリストにしましたが、それらを注文したとき、私はそれらを繰り返し比較する方法がわかりませんでした。私の問題は、並べ替えると座標がわからなくなったことです。たぶん、座標をキーにしてそれらを辞書に入れることができます。 - Ivo Flipse
  • はい、何らかの辞書が必要です。私はあなたのグリッドの表現がすでにある種の辞書であると仮定したでしょう。 - Johannes Charra
  • まあ、あなたが上で見る画像はでこぼこの配列です。残りは現在多次元リストに格納されています。私は辞書を繰り返し処理することに慣れていませんが、おそらくそれをやめることをお勧めします。 - Ivo Flipse

0

ステップバイステップで進むとどうなりますか:最初に大域的最大値を見つけ、必要に応じて周囲の点にその値を与えて処理し、次に見つかった領域をゼロに設定し、次の点に対して繰り返します。


  • うーん、そのゼロに設定することは、それ以上のどんな計算からもそれを取り除くだろう、それは有用だろう。 - Ivo Flipse
  • ゼロに設定する代わりに、手で選んだパラメータを使ってガウス関数を計算し、元の圧力測定値から検出値を減算することができます。つま先がセンサーを押している場合は、最も高い押し位置を見つけることで、つま先がセンサーに与える影響を減らすために使用します。これにより、隣接する圧力値の高いセルを排除できます。en.wikipedia.org/wiki/File:Gaussian_2d.png - Daniyar
  • サンプルデータ@Daniyarに基づいて例を表示するように注意しますか。そのような種類のデータ処理にはあまり慣れていないので - Ivo Flipse

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

リンクされた質問


最近の質問