私は犬の足の下の圧力を測定する獣医クリニックを手伝っています。私はデータ分析にPythonを使用していますが、今度は足を(解剖学的)小領域に分割しようとしています。
各足の2次元配列を作成しました。これは、足で時間の経過とともにロードされた各センサーの最大値で構成されています。これは、私が '検出'したい領域を描くためにExcelを使った一足の例です。これらは、極大値を持つセンサーの周囲2×2のボックスで、合計で最大の合計になります。
そこで私はいくつかの実験を試みて、単純に各列と行の最大値を探すことにしました(足の形のために一方向には見えません)。これは、別々のつま先の位置をかなりよく「検出」しているように見えますが、隣接するセンサーもマークしています。
それで、Pythonにこれらの最大値のどれが私が欲しいものであるかを伝えるための最善の方法は何でしょうか?
注:2x2の正方形は別々のつま先でなければならないので、重なり合うことはできません!
また、私は便利なものとして2x2を取り、より高度な解決策は大歓迎ですが、私は単に人間の運動科学者なので、私は本当のプログラマーでも数学者でもないので、それを「単純」にしてください。
これが np.loadtxt
でロードできるバージョン です
そこで、私は@ jexteeの解決策を試しました(以下の結果を参照)。見てわかるように、それは非常に前足に働きます、しかしそれは後肢にはあまりうまくいきません。
より具体的には、それは4番目のつま先である小さなピークを認識することはできません。これがどこにあるかを考慮せずに、ループが最小値に向かってトップダウンに見えるという事実に明らかに固有のものです。
誰もが@ jexteeのアルゴリズムを微調整して、4本目のつま先も見つけることができるようにする方法を知っていますか?
他の試験はまだ処理していないので、他のサンプルを提供することはできません。しかし、私が以前に与えたデータは各足の平均でした。このファイルは、それらがプレートと接触した順序で9本の足の最大データを持つ配列です。
この画像は、それらがプレート上でどのように空間的に広がっているかを示しています。
興味のある人のためにブログを開設しましたと すべての生の測定値を含むSkyDriveを開設しました。 データ:あなたにもっと力を!
それで私が 足の検出 と 足のソート に関する私の質問で手助けをした後、私はついに各足のつま先の検出をチェックすることができました!結局のところ、それは私の自身の例のもののような大きさの足以外の何にもそれほどうまくいかない。後知恵ではもちろんですが、2x2を任意に選択したのは私自身の責任です。
これは、うまくいかない場合の良い例です。爪がつま先として認識され、「かかと」が非常に広いため、2回認識されます。
足が大きすぎるので、重ならずに2 x 2のサイズを取ると、つま先が2回検出されます。反対に、小さな犬では5本目のつま先が見つからないことがよくありますが、これは2×2の面積が大きすぎることが原因と考えられます。
すべての測定値に対して現在の解決策を試してみてください _私は、ほぼすべての私の小型犬に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
を使用して、すべての異なるオブジェクトにラベルを付けるだけです。それからあなたはそれらと個別に遊ぶことができるでしょう。
注意してください背景が騒々しいされていないため、メソッドがうまく機能すること。もしそうなら、あなたはバックグラウンドで他の不要なピークの束を検出するでしょう。もう1つの重要な要素は、近傍のサイズです。ピークサイズが変わった場合は調整する必要があります(大体比例しているはずです)。
データファイル: 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
を参照)。それから結果を印刷するときに、もう一度行/列の位置を計算します。
私は2x2の正方形が重なるのを許しました。編集バージョンでは、重ならない四角形だけが結果に表示されるように、それらの一部を除外しています。
もう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回なので、すべての足について評価する場合は3960のケースがあります。
これは 画像登録問題です 。一般的な戦略は次のとおりです。
これは大まかで準備ができたアプローチです。
オリエンテーションの問題に対処するために、基本的な方向(北、北東など)に8程度の初期設定を設定できます。それぞれを個別に実行し、2つ以上のつま先が同じピクセルになるような結果はすべて捨てます。もう少し考えてみますが、このようなことはまだ画像処理で研究されています - 正しい答えはありません!
もう少し複雑な考え:(加重)K平均クラスタリングそれほど悪くない。
収束するまで繰り返します。
この方法はほぼ確実にはるかに良い結果をもたらします、そしてあなたはつま先を識別するのを助けるかもしれない各クラスタの質量を得ます。
(やはり、前もってクラスタの数を指定しました。クラスタ化では、何らかの方法で密度を指定する必要があります。この場合は適切なクラスタの数を選択するか、クラスタの半径を選択して終了数を確認します。後者の例は、 mean-shift です。
実装の詳細や他の詳細の欠如についてすみません。これをコーディングしますが、期限があります。来週までに他に何もうまくいかなかったら私に知らせてください、そして私はそれに打撃を与えます。
この問題は物理学者によってある程度深く研究されてきた。 ROOT に良い実装があります。 TSpectrum クラス(特にあなたの場合は TSpectrum2 )とそれらのドキュメントを見てください。
参考文献:
...そしてNIMの購読にアクセスできない人のために:
永続的相同性を使用してデータセットを分析すると、次のようになります(クリックすると拡大します)。
これは、この SO回答 で説明されているピーク検出方法の2次元バージョンです。上の図は、永続性によってソートされた0次元の永続的相同性クラスを単に示しています。
Scipy.misc.imresize()を使用して、元のデータセットを2倍にアップスケールしました。ただし、4つの足は1つのデータセットと見なしました。それを4つに分割すると問題が簡単になります。
方法論この背後にある考え方は非常に単純です。各ピクセルにそのレベルを割り当てる関数の関数グラフを考えてみましょう。それはこのように見えます:
今度は、継続的により低いレベルまで下がる、高さ255の水位を考えてみましょう。極大島では出生(出生)します。鞍点で2つの島が合流します。下の島は上の島に併合されると考えられます(死)。いわゆる永続ダイアグラム(0次元の相同性クラス、私たちの島)は、すべての島の出生時死亡数を表しています。
島の永続性は出生レベルと死レベルの差です。グレーの主対角線までのドットの垂直距離。この図は、残像を減らすことで島にラベルを付けます。
一番最初の写真は島の出生地です。この方法は、極大値を与えるだけでなく、上記の持続性によってそれらの「重要性」を定量化します。その場合、持続性が低すぎると、すべての島が除外されます。しかし、あなたの例では、すべての島(つまりすべての極大値)があなたが探しているピークです。
Pythonコードはここで見つけることができます。
これがアイデアです:あなたは画像の(離散的な)ラプラシアンを計算します。元の画像よりも劇的になるように、最大で(マイナスで)大きくなることを期待します。したがって、最大値を見つけやすくなります。
もう一つのアイデアがあります:あなたが高圧スポットの典型的なサイズを知っているならば、あなたは最初にそれを同じサイズのガウスで畳み込むことによってあなたのイメージを滑らかにすることができます。これにより、処理する画像が簡単になります。
頭の中からいくつかのアイデアをいくつか紹介します。
また、 OpenCV を見てみるとよいでしょう。これはかなりまともなPython APIであり、便利な機能があるかもしれません。
私はあなたが今ではもう十分に行くことができると確信しています、しかし私は助けることができませんk平均クラスタリング方法を使うことを提案します。 k-meansは教師なしのクラスタリングアルゴリズムで、データを(任意の次元で - 私はこれを3Dで行います)受け取り、それを明確な境界を持つk個のクラスタに配置します。あなたがこれらの犬歯を持っているべきである(正確に)つま先を正確に知っているので、それはここで素晴らしいです。
さらに、それはScipyで実装されていて本当にいいです( http://docs.scipy.org/doc/scipy/reference/cluster.vq.html ).
3Dクラスタを空間的に解決するために何ができるかの例を示します。
あなたがやりたいことは少し異なっています(2Dと圧力値を含みます)、しかし私はまだあなたがそれに打撃を与えることができると思います。
生データをありがとう。私は電車に乗っています、そしてこれは私が得た限りです(私の停車場は上がっています)。私はあなたの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>
物理学者の解決策:
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)
はX_i
を囲む2x2平方の平均力、alfa
は実験的にピークに達するパラメータです)
今度はMetropolis-Hastingsの魔法をやる時間です。
1。ランダムマーカーを選択して、ランダムな方向に1ピクセル移動します。
2。この移動が引き起こしたエネルギーの差dEを計算します。
3。 0から1までの一様乱数を得て、それをrと呼ぶ。
4。 dE<0
またはexp(-beta*dE)>r
の場合は、移動を受け入れて1に進みます。そうでない場合は、移動を元に戻して1に進みます。
これはマーカーが足に収束するまで繰り返されるべきです。 Betaはスキャンを最適化してトレードオフを制御するため、実験的にも最適化する必要があります。それはまたシミュレーション(シミュレーテッドアニーリング)の時間と共に絶えず増加することができる。
大きな望遠鏡で似たようなことをするとき私が使った別のアプローチがあります。
1)最高のピクセルを探します。それが得られたら、それを探して2x2に最適なもの(おそらく2x2の合計値を最大にするもの)を探すか、最も高いピクセルを中心とした4x4のサブ領域の内側に2dガウスフィットを実行します。
それからあなたが見つけたそれらの2x2ピクセルをピーク中心の周りにゼロ(あるいは多分3x3)に設定してください
1)に戻り、最も高いピークがノイズしきい値を下回るまで繰り返すか、必要なつま先がすべてあります
大まかな概要...
あなたはおそらく各足領域を分離するために連結成分アルゴリズムを使用したいと思うでしょう。 wikiはこれについて(いくらかのコードで)適切な説明を持っています: http://en.wikipedia.org/wiki/Connected_Component_Labeling
4つか8つの関連性を使用するかどうかについて決定を下す必要があります。個人的には、ほとんどの問題で私は6連結性を好む。とにかく、各「足跡」をつながった領域として分離したら、その領域を繰り返し処理して最大値を見つけるのは簡単なはずです。最大値を見つけたら、それを特定の「つま先」として識別するために、あらかじめ決められたしきい値に達するまで領域を繰り返し拡大することができます。
ここで微妙な問題は、コンピュータビジョン技術を使用して何かを右/左/前/後足として識別し始め、個々のつま先を見始めるとすぐに、回転、スキュー、および翻訳を考慮に入れなければならないことです。これはいわゆる「モーメント」の分析を通して達成されます。ビジョンアプリケーションで考慮すべきいくつかの異なる瞬間があります。
中心モーメント:並進不変正規化モーメント:スケーリングおよび並進不変huモーメント:並進、スケール、回転不変
瞬間に関するより多くの情報は、ウィキで "image moment"を検索することで見つけることができます。
トレーニングデータを作成できる場合は、おそらくニューラルネットワークを試してみる価値があります。ただし、これには手作業で注釈を付けた多くのサンプルが必要です。
おそらくあなたはガウス混合モデルのようなものを使うことができます。これはGMMをするためのPythonパッケージです(ちょうどGoogle検索をしました) http://www.ar.media.kyoto-u.ac.jp/members/david/softwares/em/
それはあなたがjetxeeのアルゴリズムを使って少しカンニングすることができるようです。彼は最初の3本のつま先を問題なく見つけています、そしてあなたは4本目がどこに基づいているかを推測できるはずです。
興味深い問題です。私が試みる解決策は以下の通りです。
2Dガウスマスクとの畳み込みなど、ローパスフィルタを適用します。これはあなたに(おそらく、必ずしも浮動小数点ではない)値の束を与えるでしょう。
各パウパッド(またはつま先)の既知のおおよその半径を使用して、2Dの最大ではない抑制を実行します。
これは、互いに接近している複数の候補を持つことなくあなたに最大のポジションを与えるべきです。明確にするために、ステップ1のマスクの半径もステップ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
さて、ここにいくつかの単純でひどく効率的でないコードがあります、しかしデータセットのこのサイズのためにそれは問題ありません。
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×2の各正方形の合計を使って配列を作成し、それを合計でソートします。私はそれから争いから最大の合計を持つ2x2の正方形を取り、それをbest
配列に入れて、そしてこの削除された2x2の正方形のいずれかの部分を使った他のすべての2x2の正方形を取り除きます。
最後の足(あなたの最初の写真の一番右にある合計が最も小さいもの)を除いてはうまくいくようです、より大きい合計を持つ2つの他の適格な2x2平方があることがわかります(そしてそれらは等しい合計を持っています)お互い)。そのうちの1つはまだあなたの2x2の正方形から1つの正方形を選択していますが、もう一方は左側にありません。幸いなことに、幸運にも私たちはあなたが望むものをもっと選択しているように見えますが、これはあなたがいつも欲しいものを得るために他のアイデアを使う必要があるかもしれません。
たぶんここで素朴なアプローチで十分です:あなたの平面上のすべての2x2の正方形のリストを作り、それらを合計して(降順で)順序付けましょう。
まず、あなたの "足のリスト"に最も高い値の正方形を選択してください。次に、以前に見つかった正方形のいずれとも交差しない次善の正方形のうちの4つを繰り返し選択します。
これが質問に答えているかどうかはわかりませんが、隣接していない最高の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のソース抽出の課題も検索できます。
...
段階的に進む場合はどうなりますか:最初に大域的最大値を見つけ、必要に応じて周囲の点にその値を与えて処理し、次に見つかった領域をゼロに設定し、次の点に対して繰り返します。