足を並べ替える方法は?


121

、私の前の質問私は優秀な答えだ足がプレッシャープレートを打つところ、私は検出助けたが、今はそれらに対応する足に、これらの結果をリンクするために苦労しています:

代替テキスト

足に手動で注釈を付けました(RF =右前、RH =右後、LF =左前、LH =左後)。

ご覧のとおり、繰り返しパターンがはっきりとあり、ほとんどすべての測定でそのパターンが再現されます。これは、手動で注釈が付けられた6つの試験のプレゼンテーションへのリンクです。

私の最初の考えは、次のようにヒューリスティックを使用してソートを行うことでした。

  • 前足と後足の間の重量負担は約60〜40%です。
  • 後足の表面は一般的に小さくなっています。
  • 足は(しばしば)空間的に左と右に分かれています。

ただし、私は自分のヒューリスティックについて少し懐疑的です。思いもよらなかったバリエーションに遭遇するとすぐに失敗するからです。彼らはまた、おそらく独自のルールを持っている下手な犬からの測定に対処することができません。

さらに、ジョーによって提案された注釈は時々めちゃくちゃになり、足が実際にどのように見えるかを考慮に入れません。

足のピーク検出についての質問で受け取った回答に基づいて、を分類するためのより高度なソリューションがあることを願っています。特に、圧力分布とその進行は、指紋のように、足ごとに異なるためです。発生順に並べ替えるのではなく、これを使用して足をクラスタリングできる方法があるといいのですが。

代替テキスト

したがって、対応する足で結果を並べ替えるより良い方法を探しています。

挑戦する人のために、各足の圧力データ(測定によってまとめられています)とその場所(プレート上の場所と時間内の場所)を表すスライスを含むすべてのスライスされた配列を含む辞書ピクルスにしました

明確にするために、walk_sliced_dataは、測定の名前である['ser_3'、 'ser_2'、 'sel_1'、 'sel_2'、 'ser_1'、 'sel_3']を含む辞書です。各測定値には、抽出された影響を表す別の辞書[0、1、2、3、4、5、6、7、8、9、10](「sel_1」の例)が含まれています。

また、足が部分的に(空間または時間内で)測定されている場合などの「誤った」影響は無視できます。それらはパターンの認識を助けることができるので有用ですが、分析されません。

そして、興味がある人のために、私はプロジェクトに関するすべての更新をブログに保管しています!


1
ええ、私が使っていたアプローチはうまくいきません。詳しく説明するために、私が使用していたアプローチは、影響を順序付けることであり、最初に触れる足は5番目の足と同じである、などと仮定します。(つまり、影響を注文し、4を法として使用します)。これに関する問題は、最初の足が接地した後、後足がセンサーパッドに衝突することがあります。その場合、最初に衝撃を与える足が4番目または3番目の衝撃を与える足と一致します。うまくいけば、これはいくつかの意味があります。
Joe Kington、

1
各後足のつま先が他の足よりもはるかに少ない圧力を加えるという点で、画像を正しく解釈しているでしょうか?つま先は常に「内側」、つまり犬の重心に向かっているようにも見えます。これをヒューリスティックとして組み込んでもらえますか?
トーマスラングストン2010

1
限られた画像処理スキルはやや錆びていると認めますが、各足の大きな中央パッドの最も急な勾配を簡単に取ることができますか?:少なくとも険しの角度は(足のための手書きの例が掲載非常に役立つだろうと思われるimgur.com/y2wBC imgur.com/yVqVU imgur.com/yehOc imgur.com/q0tcD
user470379

内のデータがどのようにwalk_sliced_data構造化されているかを親切に説明していただけませんか?3D配列の辞書の辞書が表示されます。3次元を修正し、最初の2次元をイメージとしてプロットすると、足が見えると思います。
スティーブTjoa

@トーマス、はい、各足は明確に異なる方法でロードされます。プログラムで何をしたいのかはわかっていますが、プログラムの方法がわかりません... @Steve、下部に説明を追加しました:-)
Ivo Flipse

回答:


123

よし!ようやく、なんとか一貫して動作するようになりました この問題が数日間私を引き込みました...楽しいものです!この回答の長さで申し訳ありませんが、私はいくつかのことについて少し詳しく説明する必要があります...

補足として、私はIvo 元の質問リンクを提供した完全なデータセットを使用しています。これは、一連のrarファイル(1匹につき1匹)であり、それぞれにASCII配列として保存されたいくつかの異なる実験実行が含まれています。スタンドアロンコードの例をこの質問にコピーアンドペーストするのではなく、完全なスタンドアロンコードを含むbitbucket mercurialリポジトリ次に示します。あなたはそれをクローンすることができます

hg clone https://joferkington@bitbucket.org/joferkington/paw-analysis


概観

質問で述べたように、問題に取り組む方法は基本的に2つあります。実際には両方を異なる方法で使用します。

  1. 足の影響の(時間的および空間的)順序を使用して、どの足がどれであるかを決定します。
  2. 純粋にその形状に基づいて「足跡」を識別してみてください。

基本的に、最初の方法は、犬の足が上記のIvoの質問に示されている台形のようなパターンに従う場合に機能しますが、足がそのパターンに従わない場合は常に失敗します。機能しない場合は、プログラムで簡単に検出できます。

したがって、トレーニングデータセット(約30匹の犬による約2000足の影響)を構築して、どの足がどれであるかを認識するために機能した場所の測定値を使用して、問題を監視された分類(追加のしわがある場合)に減らすことができます。 ..画像認識は、「通常の」教師付き分類問題よりも少し難しいです)。


パターン分析

最初の方法を詳しく説明すると、犬が普通に歩いている(走っていない!)とき(これらの犬の一部はそうでない場合があります)、前足、後右、前右、後左の順序で足に影響を与えると予想されます。 、前左など。パターンは前左足または前右足のいずれかで開始できます。

これが常に当てはまる場合は、最初の接触時間で影響を並べ替え、モジュロ4を使用して足でグループ化します。

通常の影響シーケンス

ただし、すべてが「正常」である場合でも、これは機能しません。これは、パターンの台形のような形状によるものです。後足は前の前足の後ろに空間的に落ちます。

したがって、最初の前足の衝撃後の後ろ足の衝撃は、センサープレートから外れることが多く、記録されません。同様に、足の最後の衝撃は、センサープレートから外れて発生して記録されなかった前の足の衝撃と同様に、多くの場合、シーケンスの次の足ではありません。

逃した後足

それにもかかわらず、足のインパクトパターンの形状を使用して、いつこれが発生したか、および前足が左か右かを確認できます。(ここでは、最後の影響に関する問題は無視しています。ただし、追加するのはそれほど難しくありません。)

def group_paws(data_slices, time):   
    # Sort slices by initial contact time
    data_slices.sort(key=lambda s: s[-1].start)

    # Get the centroid for each paw impact...
    paw_coords = []
    for x,y,z in data_slices:
        paw_coords.append([(item.stop + item.start) / 2.0 for item in (x,y)])
    paw_coords = np.array(paw_coords)

    # Make a vector between each sucessive impact...
    dx, dy = np.diff(paw_coords, axis=0).T

    #-- Group paws -------------------------------------------
    paw_code = {0:'LF', 1:'RH', 2:'RF', 3:'LH'}
    paw_number = np.arange(len(paw_coords))

    # Did we miss the hind paw impact after the first 
    # front paw impact? If so, first dx will be positive...
    if dx[0] > 0: 
        paw_number[1:] += 1

    # Are we starting with the left or right front paw...
    # We assume we're starting with the left, and check dy[0].
    # If dy[0] > 0 (i.e. the next paw impacts to the left), then
    # it's actually the right front paw, instead of the left.
    if dy[0] > 0: # Right front paw impact...
        paw_number += 2

    # Now we can determine the paw with a simple modulo 4..
    paw_codes = paw_number % 4
    paw_labels = [paw_code[code] for code in paw_codes]

    return paw_labels

これらすべてにもかかわらず、それは頻繁に正しく機能しません。完全なデータセット内の犬の多くは走っているように見え、足の影響は犬が歩いているときと同じ時間的順序に従っていません。(または、おそらく犬は深刻な腰の問題を抱えている...)

異常な影響シーケンス

幸いにも、足の影響が予想される空間パターンに従うかどうかをプログラムで検出できます。

def paw_pattern_problems(paw_labels, dx, dy):
    """Check whether or not the label sequence "paw_labels" conforms to our
    expected spatial pattern of paw impacts. "paw_labels" should be a sequence
    of the strings: "LH", "RH", "LF", "RF" corresponding to the different paws"""
    # Check for problems... (This could be written a _lot_ more cleanly...)
    problems = False
    last = paw_labels[0]
    for paw, dy, dx in zip(paw_labels[1:], dy, dx):
        # Going from a left paw to a right, dy should be negative
        if last.startswith('L') and paw.startswith('R') and (dy > 0):
            problems = True
            break
        # Going from a right paw to a left, dy should be positive
        if last.startswith('R') and paw.startswith('L') and (dy < 0):
            problems = True
            break
        # Going from a front paw to a hind paw, dx should be negative
        if last.endswith('F') and paw.endswith('H') and (dx > 0):
            problems = True
            break
        # Going from a hind paw to a front paw, dx should be positive
        if last.endswith('H') and paw.endswith('F') and (dx < 0):
            problems = True
            break
        last = paw
    return problems

したがって、単純な空間分類が常に機能するとは限りませんが、妥当な信頼度で機能する場合を判断できます。

トレーニングデータセット

正しく機能したパターンベースの分類から、正しく分類された足の非常に大規模なトレーニングデータセットを構築できます(32種類の犬による2400足の影響!)。

これで、「平均的な」左前部などの足の様子を見ることができます。

これを行うには、犬と同じ次元の「足のメトリック」のようなものが必要です。(完全なデータセットには、非常に大きな犬と非常に小さな犬の両方がいます!)アイリッシュエルクハウンドの足跡は、おもちゃのプードルの足跡よりも幅が広く、重くなります。a)ピクセル数が同じで、b)圧力値が標準化されるように、各足跡を再スケーリングする必要があります。これを行うために、各足跡を20x20グリッドに再サンプリングし、足の衝撃に対する最大、最小、および平均の圧力値に基づいて圧力値を再スケーリングしました。

def paw_image(paw):
    from scipy.ndimage import map_coordinates
    ny, nx = paw.shape

    # Trim off any "blank" edges around the paw...
    mask = paw > 0.01 * paw.max()
    y, x = np.mgrid[:ny, :nx]
    ymin, ymax = y[mask].min(), y[mask].max()
    xmin, xmax = x[mask].min(), x[mask].max()

    # Make a 20x20 grid to resample the paw pressure values onto
    numx, numy = 20, 20
    xi = np.linspace(xmin, xmax, numx)
    yi = np.linspace(ymin, ymax, numy)
    xi, yi = np.meshgrid(xi, yi)  

    # Resample the values onto the 20x20 grid
    coords = np.vstack([yi.flatten(), xi.flatten()])
    zi = map_coordinates(paw, coords)
    zi = zi.reshape((numy, numx))

    # Rescale the pressure values
    zi -= zi.min()
    zi /= zi.max()
    zi -= zi.mean() #<- Helps distinguish front from hind paws...
    return zi

これがすべて終わった後、私たちはようやく、平均的な左前、右後などの足の様子を見ることができます。これは、サイズが大きく異なる30匹を超える犬の平均であり、一貫した結果が得られているようです。

平均足

ただし、これらの分析を行う前に、平均(すべての犬のすべての脚の平均足)を差し引く必要があります。

平均足

これで、平均との違いを分析できるようになりました。これは認識が少し簡単です。

差動足

画像ベースの足の認識

OK ...ようやく、足を突き合わせるためのパターンセットができました。各足はpaw_image、これらの4つの400次元ベクトルと比較できる(関数によって返される)400次元ベクトルとして扱うことができます。

残念ながら、「通常の」監視付き分類アルゴリズムを使用するだけの場合(つまり、4つのパターンのうちどれが特定の足跡に最も近いかを単純な距離を使用して見つける)は、一貫して機能しません。実際、トレーニングデータセットのランダムチャンスよりもはるかに優れています。

これは、画像認識の一般的な問題です。入力データの高次元性と画像のやや「あいまい」な性質(つまり、隣接するピクセルの共分散が高い)のため、テンプレート画像と画像の違いを単に見ただけでは、それらの形の類似性。

アイゲンポーズ

これを回避するには、一連の「eigenpaws」を作成し(顔認識の「eigenfaces」のように)、各足跡をこれらのeigenpawsの組み合わせとして記述する必要があります。これは主成分分析と同じであり、基本的にデータの次元を削減する方法を提供するため、距離は形状の適切な尺度になります。

ディメンション(2400と400)よりもトレーニングイメージが多いため、速度を上げるために「ファンシーな」線形代数を実行する必要はありません。トレーニングデータセットの共分散行列を直接操作できます。

def make_eigenpaws(paw_data):
    """Creates a set of eigenpaws based on paw_data.
    paw_data is a numdata by numdimensions matrix of all of the observations."""
    average_paw = paw_data.mean(axis=0)
    paw_data -= average_paw

    # Determine the eigenvectors of the covariance matrix of the data
    cov = np.cov(paw_data.T)
    eigvals, eigvecs = np.linalg.eig(cov)

    # Sort the eigenvectors by ascending eigenvalue (largest is last)
    eig_idx = np.argsort(eigvals)
    sorted_eigvecs = eigvecs[:,eig_idx]
    sorted_eigvals = eigvals[:,eig_idx]

    # Now choose a cutoff number of eigenvectors to use 
    # (50 seems to work well, but it's arbirtrary...
    num_basis_vecs = 50
    basis_vecs = sorted_eigvecs[:,-num_basis_vecs:]

    return basis_vecs

これらbasis_vecsが「固有足」です。

アイゲンポーズ

これらを使用するには、各足の画像(20x20の画像ではなく、400次元のベクトルとして)に基底ベクトルをドット(つまり、行列の乗算)します。これにより、画像の分類に使用できる50次元ベクトル(基底ベクトルごとに1つの要素)が得られます。20x20の画像を各「テンプレート」の足の20x20の画像と比較する代わりに、50次元の変換された画像を各50次元の変換されたテンプレートの足と比較します。これは、各つま先の正確な配置方法などの小さな変動に対する感度がはるかに低く、基本的に問題の次元を関連する次元にまで減らします。

アイゲンポーに基づく足の分類

これで、各脚の50次元ベクトルと「テンプレート」ベクトルの間の距離を使用して、どの足がどれであるかを分類できます。

codebook = np.load('codebook.npy') # Template vectors for each paw
average_paw = np.load('average_paw.npy')
basis_stds = np.load('basis_stds.npy') # Needed to "whiten" the dataset...
basis_vecs = np.load('basis_vecs.npy')
paw_code = {0:'LF', 1:'RH', 2:'RF', 3:'LH'}
def classify(paw):
    paw = paw.flatten()
    paw -= average_paw
    scores = paw.dot(basis_vecs) / basis_stds
    diff = codebook - scores
    diff *= diff
    diff = np.sqrt(diff.sum(axis=1))
    return paw_code[diff.argmin()]

結果の一部を次に示します。 代替テキスト 代替テキスト 代替テキスト

残っている問題

特に犬が小さすぎて明確な足跡を作成できない場合は、まだいくつかの問題があります...(足がセンサーの解像度でより明確に分離されているので、大きな犬に最適です)。また、部分的な足跡はこれで認識されません。システム、台形パターンベースのシステムとすることができます。

ただし、固有足分析は本質的に距離メトリックを使用するため、足を両方の方法で分類し、「コードブック」からの固有足分析の最小距離がしきい値を超えている場合は、台形パターンベースのシステムにフォールバックできます。ただし、これはまだ実装していません。

ふew…それは長かった!こんなに楽しい質問をした私の帽子は、Ivoから離れています。


2
すばらしい答えです。私もアイゲンポー法を試しましたが、あなたほど忍耐力がありませんでした。私が目にする1つの問題は、足の登録です。つまり、顔の登録は顔の認識です。各足の位置と回転の正規化で問題が発生しましたか?その場合、PCAを実行する前に、足を前処理して並進回転不変機能に変換することができます。
Steve Tjoa、2011年

2
@Steve、それをさらに改善する方法についてJoeといくつかの議論がありましたが、私はそれらを回転させようとしませんでした。ただし、今のところプロジェクトを完了するために、すべての足に手動で注釈を付けてまとめました。幸いなことに、これにより、認識をより敏感にするためのさまざまなトレーニングセットを作成することもできます。足を回転させるために、つま先を使用することを計画していましたが、私のブログで読むことができるように、それは私の最初の質問がそれを次のように見せたほど簡単ではありません...
Ivo Flipse

@Basicええ、私は自分のWebサイトのホスティングに切り替え、すべてのWordpressコンテンツを移動しましたが、ここでコメントを編集することができなくなりました。あなたはそれらをここで見つけることができるはずです:flipserd.com/blog/ivoflipse/post/improving-the-paw-detection
Ivo

4

純粋に継続時間に基づいた情報を使用すると、キネマティクスのモデリングの手法を適用できると思います。つまり、インバースキネマティクス。向き、長さ、持続時間、および総重量と組み合わせると、ある程度の周期性が得られます。これは、「足の分類」の問題を解決するための最初のステップになると思います。

そのすべてのデータを使用して、境界のあるポリゴン(またはタプル)のリストを作成できます。これを使用して、ステップサイズでソートしてから、足の強さ[インデックス]でソートできます。


2

テストを実行する技術者に最初の足(または最初の2つ)を手動で入力してもらえますか?プロセスは次のとおりです。

  • 技術者にステップ画像の順序を示し、最初の足に注釈を付けるように要求します。
  • 最初の足に基づいて他の足にラベルを付け、技術者が修正を行ったり、テストを再実行したりできるようにします。これにより、足の不自由な犬や3本足の犬が可能になります。

最初の足の注釈は実際にはありますが、完璧ではありません。ただし、最初の足は常に前足であり、後足を分離するのに役立ちません。さらに、ジョーが述べたように順序は完全ではありません。なぜなら、それは最初に両方の前面がプレートに触れる必要があるからです。
Ivo Flipse 2010

注釈は画像認識を使用する場合に役立ちます。24の測定値があるため、少なくとも24の足にはすでに注釈が付けられています。それらが4つのグループにクラスター化される場合、そのうちの2つは、アルゴリズムがクラスター化をかなり確実にするのに十分な量の前足を含む必要があります。
Ivo Flipse 2010

私がそれらを間違って読んでいない限り、リンクされた注釈付きトライアルは、6つのトライアルのうち4つのトライアルで最初に後足が触れたことを示しています。
Jamie Ide

ああ、私は時間的に意味しました。ファイルをループする場合、前足が常に最初にプレートに接触する必要があります。
Ivo Flipse 2010
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.