Deteksi puncak dalam array 2D

874

Saya membantu klinik hewan mengukur tekanan di bawah kaki anjing. Saya menggunakan Python untuk analisis data saya dan sekarang saya terjebak mencoba untuk membagi cakram menjadi subregional (anatomi).

Saya membuat array 2D dari setiap kaki, yang terdiri dari nilai maksimal untuk setiap sensor yang telah dimuat oleh kaki dari waktu ke waktu. Berikut adalah contoh satu kaki, di mana saya menggunakan Excel untuk menggambar area yang ingin saya 'deteksi'. Ini adalah kotak 2 per 2 di sekitar sensor dengan maxima lokal, yang bersama-sama memiliki jumlah terbesar.

teks alternatif

Jadi saya mencoba beberapa percobaan dan memutuskan untuk hanya mencari maksimum setiap kolom dan baris (tidak dapat melihat satu arah karena bentuk paw). Ini tampaknya 'mendeteksi' lokasi jari-jari yang terpisah dengan cukup baik, tetapi juga menandai sensor tetangga.

teks alternatif

Jadi apa cara terbaik untuk memberi tahu Python yang mana dari maksimum ini yang saya inginkan?

Catatan: Kotak 2x2 tidak bisa tumpang tindih, karena harus terpisah!

Juga saya menggunakan 2x2 sebagai solusi, ada solusi yang lebih maju, tetapi saya hanya seorang ilmuwan gerakan manusia, jadi saya bukan programmer sejati atau ahli matematika, jadi tolong jaga agar tetap 'sederhana'.

Berikut ini adalah versi yang dapat dimuatnp.loadtxt


Hasil

Jadi saya mencoba solusi @ jextee (lihat hasil di bawah). Seperti yang Anda lihat, ini bekerja sangat baik di kaki depan, tetapi bekerja kurang baik untuk kaki belakang.

Lebih khusus lagi, itu tidak bisa mengenali puncak kecil itu ujung keempat. Ini jelas melekat pada kenyataan bahwa loop terlihat dari atas ke bawah menuju nilai terendah, tanpa memperhitungkan di mana ini.

Adakah yang tahu cara men-tweak algoritma @ jextee, sehingga mungkin bisa menemukan jari ke-4 juga?

teks alternatif

Karena saya belum memproses percobaan lain, saya tidak dapat menyediakan sampel lain. Tapi data yang saya berikan sebelumnya adalah rata-rata dari setiap kaki. File ini adalah larik dengan data maksimal 9 kaki sesuai urutan kontak.

Gambar ini menunjukkan bagaimana mereka tersebar secara spasial di atas piring.

teks alternatif

Memperbarui:

Saya telah membuat blog untuk siapa saja yang tertarik dan saya sudah menyiapkan SkyDrive dengan semua pengukuran mentah. Jadi bagi siapa pun yang meminta lebih banyak data: lebih banyak kekuatan untuk Anda!


Pembaruan baru:

Jadi setelah bantuan yang saya dapatkan dengan pertanyaan saya tentang deteksi kaki dan penyortiran kaki , saya akhirnya dapat memeriksa deteksi jari kaki untuk setiap kaki! Ternyata, itu tidak bekerja dengan baik dalam apa pun kecuali ukuran kaki seperti yang ada dalam contoh saya sendiri. Tentu saja di belakang, itu salah saya sendiri untuk memilih 2x2 begitu sewenang-wenang.

Ini adalah contoh yang bagus tentang kesalahannya: kuku dikenali sebagai jari kaki dan 'tumit' sangat lebar, sehingga dikenali dua kali!

teks alternatif

Kaki terlalu besar, jadi mengambil ukuran 2x2 tanpa tumpang tindih, menyebabkan beberapa jari kaki terdeteksi dua kali. Sebaliknya, pada anjing kecil seringkali gagal menemukan jari kaki ke-5, yang saya duga disebabkan oleh area 2x2 yang terlalu besar.

Setelah mencoba solusi saat ini pada semua pengukuran saya, saya sampai pada kesimpulan mengejutkan bahwa untuk hampir semua anjing kecil saya tidak menemukan jari kaki ke-5 dan bahwa lebih dari 50% dampak untuk anjing besar itu akan menemukan lebih banyak!

Jadi jelas saya perlu mengubahnya. Dugaan saya sendiri adalah mengubah ukuran neighborhoodmenjadi sesuatu yang lebih kecil untuk anjing kecil dan lebih besar untuk anjing besar. Tapi generate_binary_structuresaya tidak akan membiarkan saya mengubah ukuran array.

Oleh karena itu, saya berharap ada orang lain yang memiliki saran yang lebih baik untuk menemukan jari kaki, mungkin berskala jari kaki dengan ukuran kaki?

Ivo Flipse
sumber
Saya menganggap bahwa koma adalah tempat desimal daripada pemisah nilai?
MattH
Ya, itu koma. Dan @Christian, saya mencoba memasukkannya ke dalam file yang mudah dibaca, tetapi bahkan itu gagal pada saya :(
Ivo Flipse
3
Ketika saya melakukan studi kelayakan, semuanya berjalan dengan sangat baik. Jadi saya mencari banyak cara untuk mendefinisikan tekanan, termasuk subkawasan. Saya juga harus bisa membedakan sisi jempol dan jempol untuk memperkirakan orientasi. Tapi karena ini belum pernah dilakukan sebelumnya, tidak ada yang tahu apa yang mungkin kita temukan :-)
Ivo Flipse
2
@Ron: salah satu tujuan dari penelitian ini adalah untuk melihat berapa ukuran / berat anjing yang cocok untuk sistem, jadi ya sementara anjing ini sekitar 20 kg. Saya memiliki beberapa yang jauh lebih kecil (dan lebih besar) dan berharap bahwa saya tidak akan dapat melakukan hal yang sama untuk yang sangat kecil.
Ivo Flipse
2
@ jujur ​​kaki diukur dari waktu ke waktu, maka dimensi ke-3. Namun, mereka tidak beranjak dari posisi mereka (secara relatif) jadi saya lebih tertarik di mana jari-jari kaki berada dalam 2D. Aspek 3D datang gratis setelah itu
Ivo Flipse

Jawaban:

332

Saya mendeteksi puncak menggunakan filter maksimum lokal . Ini adalah hasil dari dataset pertama Anda 4 kaki: Hasil deteksi puncak

Saya juga menjalankannya pada dataset kedua 9 kaki dan berhasil juga .

Inilah cara Anda melakukannya:

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

Yang perlu Anda lakukan adalah menggunakan scipy.ndimage.measurements.labeltopeng untuk memberi label pada semua objek yang berbeda. Maka Anda akan dapat bermain dengan mereka secara individual.

Perhatikan bahwa metode ini berfungsi dengan baik karena latar belakangnya tidak berisik. Jika ya, Anda akan mendeteksi sekelompok puncak yang tidak diinginkan lainnya di latar belakang. Faktor penting lainnya adalah ukuran lingkungan . Anda harus menyesuaikannya jika ukuran puncaknya berubah (seharusnya tetap proporsional).

Ivan
sumber
1
Ada solusi yang lebih sederhana daripada (eroded_background ^ local_peaks). Lakukan saja (latar depan & puncak lokal)
Ryan Soklaski
53

Larutan

File data: paw.txt . Kode sumber:

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"

Keluaran tanpa kotak tumpang tindih. Tampaknya area yang sama dipilih seperti pada contoh Anda.

Beberapa komentar

Bagian yang sulit adalah untuk menghitung jumlah semua kotak 2x2. Saya berasumsi Anda membutuhkan semuanya, jadi mungkin ada beberapa yang tumpang tindih. Saya menggunakan irisan untuk memotong kolom pertama / terakhir dan baris dari array 2D asli, dan kemudian tumpang tindih semuanya bersama-sama dan menghitung jumlah.

Untuk memahaminya dengan lebih baik, gambar array 3x3:

>>> a = arange(9).reshape(3,3) ; a
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

Kemudian Anda dapat mengambil irisannya:

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

Sekarang bayangkan Anda menumpuknya satu di atas yang lain dan menjumlahkan elemen pada posisi yang sama. Jumlah ini akan menjadi jumlah yang sama persis di atas kotak 2x2 dengan sudut kiri atas di posisi yang sama:

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

Saat Anda memiliki jumlah lebih dari kotak 2x2, Anda dapat menggunakan maxuntuk menemukan maksimum, atau sort, atau sorteduntuk menemukan puncak.

Untuk mengingat posisi puncak, saya memasangkan setiap nilai (jumlah) dengan posisi ordinalnya dalam array yang rata (lihat zip ). Kemudian saya menghitung posisi baris / kolom lagi ketika saya mencetak hasilnya.

Catatan

Saya mengizinkan kotak 2x2 untuk tumpang tindih. Versi yang diedit menyaring beberapa dari mereka sehingga hanya kotak yang tidak tumpang tindih yang muncul dalam hasil.

Memilih jari (ide)

Masalah lain adalah bagaimana memilih apa yang mungkin menjadi jari dari semua puncak. Saya punya ide yang mungkin berhasil atau tidak. Saya tidak punya waktu untuk mengimplementasikannya sekarang, jadi pseudo-code saja.

Saya perhatikan bahwa jika jari-jari depan tetap berada pada lingkaran yang hampir sempurna, jari belakang harus berada di dalam lingkaran itu. Juga, jari-jari depan memiliki jarak yang kurang lebih sama. Kita dapat mencoba menggunakan sifat heuristik ini untuk mendeteksi jari.

Kode palsu:

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

Ini adalah pendekatan brute-force. Jika N relatif kecil, maka saya pikir itu bisa dilakukan. Untuk N = 12, ada C_12 ^ 5 = 792 kombinasi, kali 5 cara untuk memilih jari belakang, jadi 3960 kasus untuk dievaluasi untuk setiap kaki.

sastanin
sumber
Dia harus menyaring cakarnya secara manual, mengingat daftar hasil Anda ... memilih empat hasil teratas akan memberinya empat kemungkinan untuk membangun kotak 2x2 yang berisi nilai maksimum 6,8
Johannes Charra
Kotak 2x2 tidak dapat tumpang tindih, karena jika saya ingin melakukan statistik, saya tidak ingin menggunakan wilayah yang sama, saya ingin membandingkan wilayah :-)
Ivo Flipse
Saya mengedit jawabannya. Sekarang tidak ada kotak yang tumpang tindih dalam hasilnya.
sastanin
1
Saya mencobanya dan tampaknya bekerja untuk cakram depan, tetapi kurang untuk yang di belakang. Kira kita harus mencoba sesuatu yang tahu ke mana harus mencari
Ivo Flipse
1
Saya menjelaskan ide saya bagaimana jari dapat dideteksi dalam kode semu. Jika Anda suka, saya dapat mencoba menerapkannya besok malam.
sastanin
34

Ini adalah masalah registrasi gambar . Strategi umum adalah:

  • Memiliki contoh yang diketahui, atau semacam sebelumnya pada data.
  • Cocokkan data Anda dengan contoh, atau cocokkan contoh dengan data Anda.
  • Ini membantu jika data Anda secara rata disejajarkan.

Inilah pendekatan kasar dan siap , "hal terbodoh yang mungkin bisa berhasil":

  • Mulailah dengan koordinat lima jari kaki di tempat yang Anda harapkan.
  • Dengan masing-masing, iteratif naik ke puncak bukit. yaitu diberi posisi saat ini, pindah ke piksel tetangga maksimum, jika nilainya lebih besar dari piksel saat ini. Berhentilah saat koordinat jari kaki Anda berhenti bergerak.

Untuk mengatasi masalah orientasi, Anda dapat memiliki 8 atau lebih pengaturan awal untuk arah dasar (Utara, Timur Laut, dll). Jalankan masing-masing secara terpisah dan buang semua hasil di mana dua jari atau lebih berakhir pada piksel yang sama. Saya akan memikirkan ini lagi, tetapi hal semacam ini masih diteliti dalam pemrosesan gambar - tidak ada jawaban yang benar!

Gagasan yang sedikit lebih rumit: K-means clustering. Tidak seburuk itu.

  • Mulailah dengan koordinat lima jari kaki, tetapi sekarang ini adalah "pusat cluster".

Kemudian beralih hingga konvergensi:

  • Tetapkan setiap piksel ke kluster terdekat (cukup buat daftar untuk setiap kluster).
  • Hitung pusat massa masing-masing cluster. Untuk setiap cluster, ini adalah: Jumlah (koordinat * nilai intensitas) / Jumlah (koordinat)
  • Pindahkan setiap cluster ke pusat massa baru.

Metode ini hampir pasti akan memberikan hasil yang jauh lebih baik, dan Anda mendapatkan massa masing-masing cluster yang dapat membantu dalam mengidentifikasi jari-jari kaki.

(Sekali lagi, Anda telah menentukan jumlah cluster di depan. Dengan pengelompokan Anda harus menentukan kepadatan dengan satu atau lain cara: Baik memilih jumlah cluster, sesuai dalam kasus ini, atau memilih jari-jari cluster dan melihat berapa banyak Anda berakhir dengan. Contoh yang terakhir adalah mean-shift .)

Maaf tentang kurangnya detail implementasi atau spesifikasi lainnya. Saya akan membuat kode ini tetapi saya punya tenggat waktu. Jika tidak ada yang berhasil minggu depan, beri tahu saya dan saya akan mencobanya.

CakeMaster
sumber
1
Masalahnya adalah, bahwa paw mengubah orientasi mereka dan saya tidak memiliki kalibrasi / baseline paw yang benar untuk memulai. Ditambah lagi, saya takut bahwa banyak algoritma pengenalan gambar sedikit keluar dari liga saya.
Ivo Flipse
Pendekatan "kasar dan siap" cukup sederhana - mungkin saya tidak mengetahuinya dengan baik. Saya akan memasukkan beberapa pseudocode untuk menggambarkan.
CakeMaster
Saya merasa saran Anda akan membantu memperbaiki pengenalan kaki belakangnya, saya hanya tidak tahu 'bagaimana'
Ivo Flipse
Saya telah menambahkan ide lain. Ngomong-ngomong, jika Anda memiliki banyak data yang bagus, akan lebih baik untuk meletakkannya secara online di suatu tempat. Ini bisa bermanfaat bagi orang yang mempelajari pemrosesan gambar / pembelajaran mesin dan Anda mungkin mendapatkan lebih banyak kode dari itu ...
CakeMaster
1
Saya hanya berpikir tentang menuliskan pemrosesan data saya di blog Wordpress sederhana, hanya untuk digunakan untuk orang lain dan saya harus menuliskannya juga. Saya suka semua saran Anda, tetapi saya khawatir saya harus menunggu seseorang tanpa batas waktu ;-)
Ivo Flipse
18

Dengan menggunakan homologi persisten untuk menganalisis set data Anda, saya mendapatkan hasil berikut (klik untuk memperbesar):

Hasil

Ini adalah versi 2D dari metode deteksi puncak yang dijelaskan dalam jawaban SO ini . Gambar di atas hanya menunjukkan kelas homologi persisten 0-dimensi yang diurutkan berdasarkan kegigihan.

Saya melakukan peningkatan pada dataset asli dengan faktor 2 menggunakan scipy.misc.imresize (). Namun, perhatikan bahwa saya menganggap empat cakar sebagai satu dataset; membaginya menjadi empat akan membuat masalah lebih mudah.

Metodologi. Gagasan di balik ini cukup sederhana: Pertimbangkan grafik fungsi dari fungsi yang menetapkan setiap piksel levelnya. Ini terlihat seperti ini:

Grafik fungsi 3D

Sekarang perhatikan ketinggian air pada ketinggian 255 yang terus-menerus turun ke tingkat yang lebih rendah. Di pulau-pulau maxima lokal muncul (lahir). Pada titik sadel dua pulau bergabung; kami menganggap pulau yang lebih rendah untuk bergabung ke pulau yang lebih tinggi (kematian). Diagram kegigihan yang disebut (dari kelas homologi dimensi ke-0, pulau-pulau kita) menggambarkan nilai-nilai kelahiran-mati dari semua pulau:

Diagram kegigihan

The ketekunan dari sebuah pulau kemudian perbedaan antara sejak lahir dan kematian-tingkat; jarak vertikal sebuah titik ke diagonal utama abu-abu. Sosok itu memberi label pulau-pulau dengan mengurangi kegigihan.

Gambar pertama menunjukkan lokasi kelahiran pulau-pulau. Metode ini tidak hanya memberikan maksimum lokal tetapi juga mengukur "signifikansi" mereka dengan kegigihan yang disebutkan di atas. Satu kemudian akan menyaring semua pulau dengan ketekunan yang terlalu rendah. Namun, dalam contoh Anda, setiap pulau (yaitu, setiap maksimum lokal) adalah puncak yang Anda cari.

Kode python dapat ditemukan di sini .

S. Huber
sumber
16

Masalah ini telah dipelajari secara mendalam oleh fisikawan. Ada implementasi yang baik di ROOT . Lihatlah kelas TSpectrum (khususnya TSpectrum2 untuk kasus Anda) dan dokumentasi untuk mereka.

Referensi:

  1. M.Morhac et al.: Metode eliminasi latar belakang untuk spektrum gamma-ray kebetulan multidimensi. Instrumen dan Metode Nuklir dalam Penelitian Fisika A 401 (1997) 113-132.
  2. M.Morhac et al .: Dekonvolusi emas satu dan dua dimensi yang efisien dan aplikasinya pada dekomposisi spektrum sinar gamma. Instrumen dan Metode Nuklir dalam Penelitian Fisika A 401 (1997) 385-408.
  3. M.Morhac et al.: Identifikasi puncak dalam spektrum gamma-ray kebetulan multidimensi. Instrumen dan Metode Nuklir dalam Fisika Penelitian A 443 (2000), 108-125.

... dan bagi mereka yang tidak memiliki akses ke berlangganan NIM:

dmckee --- mantan kucing moderator
sumber
Untuk melirik artikel itu sepertinya menggambarkan pemrosesan data yang sama seperti apa yang saya coba di sini, namun saya khawatir itu sangat melampaui kemampuan pemrograman saya :(
Ivo Flipse
@Ivo: Saya belum pernah mencoba mengimplementasikannya sendiri. Saya hanya menggunakan ROOT. Ada binding python, tidak ada yang kurang, tetapi perlu diketahui bahwa ROOT adalah paket yang cukup berat.
dmckee --- ex-moderator kitten
@Ivo Flipse: Saya setuju dengan dmckee. Anda memiliki banyak petunjuk yang menjanjikan dalam jawaban lain. Jika semuanya gagal dan Anda merasa ingin menginvestasikan waktu, Anda dapat mempelajari ROOT dan itu (mungkin) akan melakukan apa yang Anda perlukan. Saya tidak pernah kenal siapa pun yang mencoba mempelajari ROOT melalui ikatan python (bukan C ++ alami), jadi saya berharap Anda beruntung.
physicsmichael
13

Berikut adalah sebuah ide: Anda menghitung Laplacian (diskrit) dari gambar. Saya berharap itu menjadi (negatif dan) besar di maxima, dengan cara yang lebih dramatis daripada di gambar aslinya. Dengan demikian, maxima bisa lebih mudah ditemukan.

Berikut adalah ide lain: jika Anda tahu ukuran khas dari tempat-tempat bertekanan tinggi, pertama-tama Anda dapat menghaluskan gambar dengan membelitnya dengan Gaussian dengan ukuran yang sama. Ini dapat memberi Anda gambar yang lebih sederhana untuk diproses.

Eric O Lebigot
sumber
11

Hanya beberapa ide dari kepala saya:

  • ambil gradien (turunan) dari pemindaian, lihat apakah itu menghilangkan panggilan palsu
  • ambil maksimum dari maxima lokal

Anda mungkin juga ingin melihat OpenCV , itu punya Python API yang cukup baik dan mungkin memiliki beberapa fungsi yang Anda anggap berguna.

ChrisC
sumber
Dengan gradien, maksud Anda saya harus menghitung kecuraman lereng, setelah ini mendapatkan di atas nilai tertentu saya tahu ada 'puncak'? Saya mencoba ini, tetapi beberapa jari kaki hanya memiliki puncak yang sangat rendah (1,2 N / cm) dibandingkan dengan yang lain (8 N / cm). Jadi bagaimana saya harus menangani puncak dengan gradien yang sangat rendah?
Ivo Flipse
2
Apa yang berhasil bagi saya di masa lalu jika saya tidak dapat menggunakan gradien secara langsung adalah dengan melihat gradien dan maxima, misalnya jika gradien adalah ekstrema lokal dan saya di maxima lokal, maka saya pada titik bunga.
ChrisC
11

Saya yakin Anda sudah cukup untuk melanjutkan sekarang, tetapi saya tidak bisa tidak menyarankan menggunakan metode k-means clustering. k-means adalah algoritma pengelompokan tanpa pengawasan yang akan membawa Anda data (dalam sejumlah dimensi - saya kebetulan melakukan ini dalam 3D) dan mengaturnya menjadi k cluster dengan batas-batas yang berbeda. Sangat menyenangkan di sini karena Anda tahu persis berapa banyak jari kaki yang seharusnya dimiliki oleh gigi taring ini.

Selain itu, ini diterapkan di Scipy yang sangat bagus ( http://docs.scipy.org/doc/scipy/reference/cluster.vq.html ).

Berikut adalah contoh apa yang dapat dilakukan untuk menyelesaikan klaster 3D secara spasial: masukkan deskripsi gambar di sini

Apa yang ingin Anda lakukan sedikit berbeda (2D dan termasuk nilai tekanan), tapi saya masih berpikir Anda bisa mencobanya.

astromax
sumber
10

terima kasih untuk data mentah. Saya di kereta dan ini sejauh yang saya dapat (berhenti saya akan datang). Saya memijat file txt Anda dengan regexps dan memasukkannya ke halaman html dengan beberapa javascript untuk visualisasi. Saya membagikannya di sini karena beberapa, seperti saya, mungkin merasa lebih mudah diretas daripada python.

Saya pikir pendekatan yang baik adalah skala dan rotasi invarian, dan langkah selanjutnya adalah menyelidiki campuran gaussians. (masing-masing paw pad menjadi pusat gaussian).

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

teks alternatif

Ron
sumber
1
Saya rasa ini adalah bukti konsep bahwa teknik Gaussian yang direkomendasikan dapat bekerja, sekarang jika hanya seseorang yang bisa membuktikannya dengan Python ;-)
Ivo Flipse
8

Solusi fisikawan:
Tetapkan 5 marka-kaki yang diidentifikasi oleh posisi mereka X_idan init dengan posisi acak. Tentukan beberapa fungsi energi yang menggabungkan beberapa penghargaan untuk lokasi penanda di posisi kaki dengan beberapa hukuman karena tumpang tindih penanda; Katakanlah:

E(X_i;S)=-Sum_i(S(X_i))+alfa*Sum_ij (|X_i-Xj|<=2*sqrt(2)?1:0)

( S(X_i)adalah kekuatan rata-rata dalam 2x2 persegi di sekitar X_i, alfaadalah parameter yang akan memuncak secara eksperimental)

Sekarang saatnya untuk melakukan beberapa sihir Metropolis-Hastings:
1. Pilih penanda acak dan pindahkan dengan satu piksel ke arah acak.
2. Hitung dE, perbedaan energi yang disebabkan gerakan ini.
3. Dapatkan nomor acak seragam dari 0-1 dan sebut saja r.
4. Jika dE<0atau exp(-beta*dE)>r, terima langkah dan lanjutkan ke 1; jika tidak, batalkan pemindahan dan lanjutkan ke 1.
Ini harus diulang sampai marker akan menyatu menjadi cakar. Beta mengontrol pemindaian untuk mengoptimalkan tradeoff, sehingga harus juga dioptimalkan secara eksperimental; itu juga dapat terus meningkat dengan waktu simulasi (simulated annealing).

mbq
sumber
Mau menunjukkan bagaimana ini akan bekerja pada contoh saya? Karena saya benar-benar tidak masuk ke matematika tingkat tinggi, jadi saya sudah mengalami kesulitan mengungkap formula yang Anda usulkan :(
Ivo Flipse
1
Ini matematika sekolah menengah, mungkin notasi saya hanya dikaburkan. Saya punya rencana untuk memeriksanya, jadi tetaplah disini.
mbq
4
Saya seorang fisikawan partikel. Untuk waktu yang lama alat masuk ke dalam disiplin ilmu kami disebut PAW, dan memiliki entitas yang terkait dengan grafik yang disebut "penanda". Anda dapat membayangkan betapa membingungkannya saya menemukan jawaban ini pada beberapa kali pertama ...
dmckee --- ex-moderator kitten
6

Inilah pendekatan lain yang saya gunakan ketika melakukan sesuatu yang serupa untuk teleskop besar:

1) Cari piksel tertinggi. Setelah Anda memilikinya, cari yang paling cocok untuk 2x2 (mungkin memaksimalkan jumlah 2x2), atau lakukan kecocokan gaussian 2d di dalam sub wilayah katakanlah 4x4 berpusat pada piksel tertinggi.

Kemudian atur 2x2 piksel yang Anda temukan ke nol (atau mungkin 3x3) di sekitar pusat puncak

kembali ke 1) dan ulangi sampai puncak tertinggi jatuh di bawah ambang batas kebisingan, atau Anda memiliki semua jari kaki yang Anda butuhkan

Paulus
sumber
Ingin membagikan contoh kode yang melakukan ini? Saya dapat mengikuti apa yang Anda coba lakukan, tetapi tidak tahu bagaimana cara mengkodekannya sendiri
Ivo Flipse
Saya sebenarnya berasal dari bekerja dengan Matlab, jadi ya itu sudah membantu. Tetapi jika Anda menggunakan fungsi yang benar-benar asing, mungkin sulit bagi saya untuk mereplikasinya dengan Python
Ivo Flipse
6

Mungkin patut untuk dicoba dengan jaringan saraf jika Anda dapat membuat beberapa data pelatihan ... tetapi ini membutuhkan banyak sampel yang dianotasi dengan tangan.

antirez
sumber
Jika sepadan dengan masalahnya, saya tidak keberatan membubuhi keterangan sampel besar dengan tangan. Masalah saya adalah: bagaimana saya mengimplementasikan ini, karena saya tidak tahu apa-apa tentang pemrograman jaringan saraf
Ivo Flipse
6

garis besar kasar ...

Anda mungkin ingin menggunakan algoritma komponen yang terhubung untuk mengisolasi setiap wilayah paw. wiki memiliki deskripsi yang layak tentang ini (dengan beberapa kode) di sini: http://en.wikipedia.org/wiki/Connected_Component_Labeling

Anda harus membuat keputusan tentang apakah menggunakan 4 atau 8 keterhubungan. secara pribadi, untuk sebagian besar masalah saya lebih suka koneksi-6. Lagi pula, setelah Anda memisahkan masing-masing "paw print" sebagai wilayah yang terhubung, itu akan cukup mudah untuk beralih ke wilayah tersebut dan menemukan maxima. setelah Anda menemukan maxima, Anda bisa memperbesar wilayah itu secara iteratif hingga Anda mencapai ambang yang telah ditentukan untuk mengidentifikasi itu sebagai "jari" yang diberikan.

satu masalah halus di sini adalah bahwa segera setelah Anda mulai menggunakan teknik visi komputer untuk mengidentifikasi sesuatu sebagai kaki kanan / kiri / depan / belakang dan Anda mulai melihat jari kaki individu, Anda harus mulai mengambil rotasi, skew, dan terjemahan ke dalam akun. ini dicapai melalui analisis yang disebut "momen". ada beberapa momen berbeda untuk dipertimbangkan dalam aplikasi penglihatan:

momen sentral: momen invarian terjemahan dinormalisasi: scaling dan terjemahan momen invarian hu: terjemahan, skala, dan rotasi invarian

informasi lebih lanjut tentang momen dapat ditemukan dengan mencari "momen gambar" di wiki.

Joshua
sumber
4

Sepertinya Anda bisa menipu sedikit menggunakan algoritma jetxee. Dia menemukan tiga jari kaki pertama baik-baik saja, dan Anda harus bisa menebak di mana yang keempat didasarkan itu.

geoff
sumber
4

Masalah menarik. Solusi yang akan saya coba adalah sebagai berikut.

  1. Terapkan filter low pass, seperti konvolusi dengan topeng gaussian 2D. Ini akan memberi Anda banyak nilai (mungkin, tetapi tidak harus floating point).

  2. Lakukan penindasan non-maksimal 2D menggunakan radius perkiraan yang diketahui dari setiap paw pad (atau jari kaki).

Ini akan memberi Anda posisi maksimal tanpa memiliki banyak kandidat yang berdekatan. Hanya untuk memperjelas, jari-jari topeng pada langkah 1 juga harus serupa dengan jari-jari yang digunakan pada langkah 2. Jari-jari ini bisa dipilih, atau dokter hewan dapat secara eksplisit mengukurnya sebelumnya (ini akan bervariasi dengan usia / jenis / dll).

Beberapa solusi yang disarankan (pergeseran rata-rata, jaring saraf, dan sebagainya) mungkin akan bekerja pada tingkat tertentu, tetapi terlalu rumit dan mungkin tidak ideal.

Bob Mottram
sumber
Saya memiliki 0 pengalaman dengan matriks konvolusi dan filter Gaussian, jadi apakah Anda ingin menunjukkan cara kerjanya pada contoh saya?
Ivo Flipse
3

Nah, ini beberapa kode sederhana dan tidak terlalu efisien, tetapi untuk ukuran set data ini tidak masalah.

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]

Saya pada dasarnya hanya membuat array dengan posisi kiri atas dan jumlah masing-masing 2x2 persegi dan mengurutkannya dengan jumlah. Saya kemudian mengambil kotak 2x2 dengan jumlah pertengkaran tertinggi, memasukkannya ke dalambest array, dan menghapus semua kotak 2x2 lainnya yang menggunakan bagian mana saja dari 2x2 persegi yang baru saja dihapus ini.

Tampaknya berfungsi dengan baik kecuali dengan paw terakhir (yang dengan jumlah terkecil di paling kanan dalam gambar pertama Anda), ternyata ada dua kotak 2x2 lain yang memenuhi syarat dengan jumlah yang lebih besar (dan mereka memiliki jumlah yang sama dengan satu sama lain). Salah satunya masih memilih satu kotak dari kotak 2x2 Anda, tetapi yang lainnya ke kiri. Untungnya, kita beruntung memilih lebih dari yang Anda inginkan, tetapi ini mungkin memerlukan beberapa ide lain untuk digunakan untuk mendapatkan apa yang sebenarnya Anda inginkan sepanjang waktu.

Justin Peel
sumber
Saya rasa hasil Anda sama dengan yang ada di jawaban @ Jextee. Atau setidaknya begitulah tampaknya dari saya mengujinya.
Ivo Flipse
3

Hanya ingin memberi tahu kalian ada pilihan bagus untuk menemukan maximagambar lokal dengan python:

from skimage.feature import peak_local_max

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

Thomio
sumber
1

Mungkin pendekatan naif sudah cukup di sini: Buat daftar semua kotak 2x2 di pesawat Anda, pesan dengan jumlah mereka (dalam urutan menurun).

Pertama, pilih kotak bernilai tertinggi ke dalam "daftar kaki" Anda. Kemudian, pilih 4 dari kotak terbaik berikutnya yang tidak bersinggungan dengan kotak yang sebelumnya ditemukan.

Johannes Charra
sumber
Saya benar-benar membuat daftar dengan semua jumlah 2x2, tetapi ketika saya memesannya, saya tidak tahu bagaimana membandingkannya secara iteratif. Masalah saya adalah ketika saya mengurutkannya, saya kehilangan jejak koordinat. Mungkin saya bisa memasukkannya ke dalam kamus, dengan koordinat sebagai kuncinya.
Ivo Flipse
Ya, beberapa jenis kamus akan diperlukan. Saya akan berasumsi bahwa representasi Anda dari grid sudah semacam kamus.
Johannes Charra
Nah gambar yang Anda lihat di atas adalah array numpy. Sisanya saat ini disimpan dalam daftar multidimensi. Mungkin akan lebih baik untuk berhenti melakukan itu, meskipun saya tidak terbiasa dengan iterasi kamus
Ivo Flipse
1

Ada beberapa perangkat lunak luas yang tersedia dari komunitas astronomi dan kosmologi - ini adalah bidang penelitian yang signifikan baik secara historis maupun saat ini.

Jangan khawatir jika Anda bukan astronom - ada yang mudah digunakan di luar lapangan. Misalnya, Anda dapat menggunakan astropi / fotutil:

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

[Tampaknya agak kasar untuk mengulang kode sampel pendek mereka di sini.]

Daftar teknik / paket / tautan yang mungkin tidak lengkap dan sedikit menarik diberikan di bawah ini - tambahkan lebih banyak di komentar dan saya akan memperbarui jawaban ini seperlunya. Tentu saja ada pertukaran akurasi dan sumber daya komputasi. [Jujur, ada terlalu banyak untuk memberikan contoh kode dalam satu jawaban seperti ini jadi saya tidak yakin apakah jawaban ini akan terbang atau tidak.]

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

MultiNest https://github.com/farhanferoz/MultiNest [+ pyMultiNest]

Tantangan pencarian sumber ASKAP / EMU: https://arxiv.org/abs/1509.03931

Anda juga dapat mencari tantangan ekstraksi sumber Planck dan / atau WMAP.

...

jtlz2
sumber
0

Bagaimana jika Anda melanjutkan langkah demi langkah: pertama-tama Anda menemukan maksimum global, proses jika perlu titik-titik di sekitarnya diberi nilainya, kemudian atur wilayah yang ditemukan ke nol, dan ulangi untuk yang berikutnya.

Cedric H.
sumber
Hmmm bahwa pengaturan ke nol setidaknya akan menghapusnya dari perhitungan lebih lanjut, itu akan berguna.
Ivo Flipse
Alih-alih mengatur ke nol, Anda dapat menghitung fungsi gaussian dengan parameter pilihan tangan dan kurangi nilai yang ditemukan dari pembacaan tekanan asli. Jadi jika jari kaki menekan sensor Anda, maka dengan menemukan titik penekanan tertinggi, Anda menggunakannya untuk mengurangi efek jari kaki itu pada sensor, dengan demikian, menghilangkan sel tetangga dengan nilai tekanan tinggi. en.wikipedia.org/wiki/File:Gaussian_2d.png
Daniyar
Ingin menunjukkan contoh berdasarkan data sampel saya @Daniyar? Karena saya benar-benar tidak terbiasa dengan pemrosesan data semacam itu
Ivo Flipse
0

Saya tidak yakin ini menjawab pertanyaan, tetapi sepertinya Anda bisa mencari n puncak tertinggi yang tidak memiliki tetangga.

Inilah intinya. Perhatikan bahwa itu ada di Ruby, tetapi idenya harus jelas.

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
Rick Hull
sumber
Saya akan mencoba dan melihat dan melihat apakah saya dapat mengubahnya menjadi kode Python :-)
Ivo Flipse
Harap sertakan kode dalam pos itu sendiri, daripada menautkan ke intisari, jika panjangnya masuk akal.
agf