1. ホーム
  2. python

[解決済み] 2次元アレイにおけるピーク検出

2022-03-18 19:46:13

質問事項

動物病院で犬の前足にかかる圧力を測定するのを手伝っています。私はデータ分析にPythonを使用していますが、今、前足を(解剖学的)小領域に分割しようとして行き詰っています。

それぞれの肉球の2次元配列を作成しました。この配列は、時間の経過とともに肉球に負荷がかかった各センサーの最大値で構成されています。これはある肉球の例で、Excelを使って「検出」したい領域を描画したものです。これは、ローカルな最大値を持つセンサーの周りに2×2のボックスを作り、その合計が最大となるようにしたものである。

そこで、いろいろ試してみた結果、単純に各列と行の最大値を探すことにしました(前足の形状のため、一方向を見ることができない)。これは、別々の足の指の位置をかなりよく「検出」しているようですが、近隣のセンサーもマークしています。

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

注:2x2の正方形は、別々のつま先でなければならないので、重なることはありえません

また、私は便宜上2x2としましたが、もっと高度な解答も歓迎します。私は単なる人間の運動科学者であり、本当のプログラマーでも数学者でもありませんので、「シンプル」にお願いします。

以下は で読み込むことができるバージョンです。 np.loadtxt


成果

そこで、@jexteeさんの解決策を試してみました(下記の結果をご覧ください)。ご覧のように、前足にはとても効果がありますが、後ろ足にはあまり効果がありません。

具体的には、第4趾である小さな峰を認識できないのです。これは明らかに、ループが一番低い値に向かって上から下へ見るという事実に起因しており、ここがどこであるかを考慮に入れていないのです。

どなたか、@jexteeのアルゴリズムに手を加えて、4番目のつま先も見つけられるようにする方法をご存知でしょうか?

まだ他のトライアルを処理していないので、他のサンプルは提供できません。ただ、前にあげたデータは、各足の平均値でした。このファイルは、9本の足の最大データを、プレートに接触した順に並べた配列です。

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

更新情報

ブログを開設しましたので、興味のある方はご覧ください そして すべての生計測値をOneDriveにセットアップしています。 より多くのデータを要求する人たちへ:もっと力を発揮してください。


新たに更新しました。

という質問で助けてもらった後に ポー検出 ポーソーティング ということで、ようやく全ての肉球のつま先検出を確認することができました。その結果、私の例のような大きさの前足以外では、あまりうまくいかないことがわかりました。もちろん、今にして思えば、2x2を恣意的に選択した私自身の責任です。

爪がつま先として認識され、「かかと」の部分が非常に広いため、2回認識されてしまうのです!これは、どこで間違っているのかを示す良い例です。

前足が大きすぎるため、2x2のサイズを重ならないように取ると、一部の足指が2回検出される。逆に、小型犬では5番目の足指を見つけられないことがよくありますが、これは2x2の面積が大きすぎることが原因だと思われます。

現在の解決策を私のすべての測定で試してみる ほぼすべての小型犬では5番目のつま先が見つからず、大型犬では50%以上の衝撃でもっと見つかるという、驚くべき結論に達しました。

だから、明らかに変える必要があるんです。私自身の推測では neighborhood を小型犬用には小さく、大型犬用には大きくしています。しかし generate_binary_structure は、配列のサイズを変更させてくれませんでした。

そのため、つま先の位置について、どなたかもっと良い提案があるのではないかと期待しているのですが、おそらくつま先の面積は前足のサイズに比例するのでしょうか?

解決方法は?

を使用してピークを検出しました。 ローカルマキシマムフィルタ . 最初のデータセットである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 をマスクに貼り付けて、すべての異なるオブジェクトにラベルを付けます。そうすれば、個々に遊ぶことができるようになります。

ノート この方法がうまくいくのは、背景がノイジーでないためです。もしそうであれば、背景の中に他の不要なピークがたくさん検出されるでしょう。もう一つの重要な要素は 近隣 . ピークの大きさが変わったら、調整する必要があります(ほぼ比例するはずです)。