Пікове виявлення в 2D-масиві


874

Я допомагаю ветеринарній клініці вимірювати тиск під лапою собак. Я використовую Python для аналізу даних, і тепер я застряг, намагаючись розділити лапи на (анатомічні) субрегіони.

Я створив 2D масив кожної лапи, який складається з максимальних значень для кожного датчика, який завантажений лапою з часом. Ось приклад однієї лапи, де я використовував Excel для малювання областей, які я хочу «виявити». Це 2 на 2 поля навколо датчика з локальними максимумами, які разом мають найбільшу суму.

alt текст

Тому я спробував поекспериментувати і вирішив просто шукати максимум кожного стовпця та рядка (не можу дивитись в одну сторону через форму лапи). Це, здається, досить добре «визначає» розташування окремих пальців, але це також позначає сусідні датчики.

alt текст

Отже, що було б найкращим способом сказати Python, який із цих максимумів - це те, що я хочу?

Примітка: квадрати 2х2 не можуть перетинатися, оскільки вони повинні бути окремими пальцями!

Також я сприйняв 2x2 як зручність, будь-яке вдосконалене рішення вітається, але я просто вчений з людського руху, тому я не є справжнім програмістом чи математиком, тому, будь ласка, тримайте це "просто".

Ось версія, яку можна завантажитиnp.loadtxt


Результати

Тому я спробував рішення @ jextee (дивіться результати нижче). Як бачите, він дуже добре працює на передніх лапах, але при цьому менше працює на задні лапи.

Більш конкретно, він не може розпізнати маленьку вершину, яка є четвертим пальцем. Це, очевидно, властиво тому, що цикл дивиться зверху вниз до найнижчого значення, не враховуючи, де це.

Хтось знає, як налаштувати алгоритм @ jextee, щоб він міг знайти і 4-й палець?

alt текст

Оскільки я ще не обробив жодних інших випробувань, я не можу надати жодних інших зразків. Але дані, які я наводив раніше, були середніми для кожної лапи. Цей файл - це масив із максимальними даними 9 лап у порядку, з яким вони встановили контакт із табличкою.

На цьому зображенні показано, як вони просторово розкладалися по тарілці.

alt текст

Оновлення:

Я створив блог для всіх, хто цікавиться, і я налаштував SkyDrive з усіма необмеженими вимірами. Тож тому, хто вимагає більше даних: більше енергії для вас!


Нове оновлення:

Тож після допомоги я отримав свої запитання щодо виявлення лапи та сортування лап , я нарешті змогла перевірити виявлення пальців ніг на кожній лапі! Виявляється, це не так добре працює ні в чому, окрім лап розміром, як у мого власного прикладу. Зрозуміло, позаду, я сама винна в тому, що я вибрала 2x2 довільно.

Ось чудовий приклад того, як воно піде не так: ніготь розпізнається як носок, а «п’ята» настільки широка, що її впізнають двічі!

alt текст

Лапа занадто велика, тому розмір 2х2 без перекриття призводить до виявлення деяких пальців ніг удвічі. І навпаки, у маленьких собак це часто не вдається знайти 5-й палець, який, як я підозрюю, викликаний тим, що зона 2х2 занадто велика.

Після випробування поточного рішення на всіх моїх вимірах я прийшов до приголомшливим висновку , що для майже всіх моїх маленьких собак він не знайшов 5 - й палець і що в більш ніж 50% від наслідків для великих собак були б знайти більше!

Так чітко мені потрібно це змінити. Моя власна здогадка, була зміна розміру neighborhoodна щось менше для маленьких собак і більшого для великих собак. Але generate_binary_structureне дозволив би мені змінити розмір масиву.

Тому я сподіваюся, що хтось інший має кращу пропозицію щодо розміщення пальців ніг, можливо, маючи масштаб області пальця ноги з розміром лапи?


Я вважаю, що коми - це десяткові знаки, а не роздільники значень?
MattH

Так, це коми. І @ Chhristian, я намагаюся вставити його в легко читабельний файл, але навіть це не вдається мені :(
Іво Фліпс

3
Коли я роблю дослідження техніко-економічного обґрунтування, все реально відбувається. Тому я шукаю якомога більше способів визначення тиску, включаючи субрегіони. Крім того, мені потрібно вміти розрізняти "великий палець" і "маленький палець", щоб оцінити орієнтацію. Але оскільки цього раніше не робилося, немає нічого сказати, що ми можемо знайти :-)
Іво Фліпс

2
@Ron: одна з цілей цього дослідження - дізнатися, для якого розміру / ваги собак система підходить, так що так, поки ця собака становила близько 20 кг. У мене є такі, які значно менші (і більші) і очікують, що я не зможу зробити те ж саме для справжніх маленьких.
Іво Фліпс

2
@frank лапи вимірюються з часом, отже, 3-й вимір. Однак вони не рухаються зі свого місця (відносно кажучи), тому мене в основному цікавить, де пальці ніг розташовані в 2D. Після цього 3D-аспект стає безкоштовним
Іво Фліпс

Відповіді:


331

Я виявив піки за допомогою локального максимального фільтра . Ось результат на вашому першому наборі даних із 4 лап: Результат виявлення піків

Я також запустив його на другий набір даних 9 лап, і він також працював .

Ось як це зробити:

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
Є більш просте рішення, ніж (eroded_background ^ local_peaks). Просто зробіть (передній план та місцеві вершини)
Райан Сокласкі

53

Рішення

Файл даних: 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"

Виведіть без перекриття квадратів. Здається, вибрані ті самі області, що і у вашому прикладі.

Деякі коментарі

Складна частина полягає в обчисленні сум усіх 2x2 квадратів. Я припускав, що вам потрібні всі, тому може статися певне перекриття. Я використовував фрагменти, щоб вирізати перші / останні стовпці та рядки з початкового 2D масиву, а потім перекривати їх усі разом і обчислювати суми.

Щоб зрозуміти це краще, зобразивши 3x3 масив:

>>> 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х2, у верхньому лівому куті в тому самому положенні:

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

Коли у вас є суми понад 2х2 квадрата, ви можете використовувати maxмаксимум, або sort, або sortedзнайти піки.

Щоб запам'ятати позиції вершин, я з’єдную кожне значення (суму) з порядковим положенням у сплющеному масиві (див zip ). Потім я знову обчислюю позицію рядків / стовпців, коли друкую результати.

Примітки

Я дозволив на площі 2х2 перекриватися. Відредагована версія фільтрує деякі з них так, що в результатах з’являються лише непересічні квадрати.

Вибір пальців (ідея)

Ще одна проблема - як вибрати те, що, ймовірно, буде пальцями з усіх вершин. У мене є ідея, яка може чи не може спрацювати. Я не маю часу реалізувати це зараз, тому просто псевдо-код.

Я помітив, що якщо передні пальці тримаються майже на ідеальному колі, задній палець повинен знаходитися всередині цього кола. Також передні пальці більш-менш однаково розташовані. Ми можемо спробувати використовувати ці евристичні властивості для виявлення пальців.

Псевдокод:

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 випадків для оцінки кожної лапи.


Йому доведеться фільтрувати лапи вручну, враховуючи ваш список результатів ... вибір чотирьох найвищих результатів дасть йому чотири можливості побудувати квадрат 2х2, який містить максимальне значення 6,8
Йоханнес Чарра

Поле 2x2 не може перекриватися, оскільки якщо я хочу робити статистику, я не хочу використовувати той самий регіон, я хочу порівнювати регіони :-)
Іво Фліпс

Я відредагував відповідь. Тепер в результатах немає квадратів, що перетинаються.
зустріч

1
Я спробував це і, здається, працює на передніх лапах, але менше - на задніх. Здогадайтесь, нам доведеться спробувати щось, що знає, куди шукати
Іво Фліпс

1
Я пояснив свою ідею, як пальці можна виявити в псевдокоді. Якщо вам це подобається, я можу спробувати реалізувати його завтра ввечері.
зустріч

34

Це проблема реєстрації зображень . Загальна стратегія:

  • Майте відомий приклад або якусь попередню інформацію щодо даних.
  • Приміряйте свої дані до прикладу або прикладайте приклад до своїх даних.
  • Це допомагає, якщо ваші дані в першу чергу приблизно вирівняні.

Ось приблизний і готовий підхід , "найглуміша річ, яка могла б працювати":

  • Почніть з п’яти координат пальців ноги приблизно в тому місці, яке ви очікуєте.
  • З кожним із них ітераційно піднімаються на вершину пагорба. тобто задане поточне положення перемістіть до максимального сусіднього пікселя, якщо його значення більше поточного пікселя. Зупиніться, коли координати пальців ніг перестають рухатися.

Щоб протистояти проблемі орієнтації, ви можете мати 8 або більше початкових налаштувань для основних напрямків (Північ, Північний Схід тощо). Запустіть кожен окремо і викиньте будь-які результати, коли два або більше пальців кінця в одному пікселі. Я подумаю про це ще дещо, але подібний предмет ще вивчається в обробці зображень - правильних відповідей немає!

Трохи складніша ідея: (зважена) K-означає кластеризація. Це не так вже й погано.

  • Почніть з п’яти координат пальців, але тепер це "центри кластерів".

Потім повторіть до конвергенції:

  • Призначте кожен піксель найближчому кластеру (просто складіть список для кожного кластера).
  • Обчисліть центр маси кожного кластера. Для кожного кластеру це: Sum (координата * значення інтенсивності) / Sum (координата)
  • Перемістіть кожен кластер до нового центру маси.

Цей метод майже напевно дасть набагато кращі результати, і ви отримуєте масу кожного скупчення, що може допомогти в визначенні пальців ніг.

(Знову ж, ви вказали кількість кластерів попереду. За допомогою кластеризації вам потрібно вказати щільність так чи інакше: або виберіть кількість кластерів, відповідне в цьому випадку, або виберіть радіус кластера і подивіться, скільки ви закінчите Приклад останнього - середній зсув .)

Вибачте за відсутність деталей щодо імплементації чи інших специфік. Я б це зашифрував, але у мене є термін. Якщо до наступного тижня більше нічого не вийшло, дайте мені знати, і я пострілю.


1
Проблема полягає в тому, що лапи змінюють свою орієнтацію, і я не маю жодної калібрування / базової лінії правильної лапи для початку. Плюс я боюся, що багато алгоритмів розпізнавання зображень трохи не в моїй лізі.
Іво Фліпсе

"Грубий і готовий" підхід досить простий - можливо, я не гадав ідеї. Я вкладу псевдокод для ілюстрації.
CakeMaster

У мене є відчуття, що ваша пропозиція допоможе виправити розпізнавання задніх лап, я просто не знаю "як"
Іво Фліпс

Я додав ще одну ідею. До речі, якщо у вас є багато хороших даних, було б здорово розмістити їх кудись в Інтернеті. Це може бути корисно для людей, які навчаються обробці зображень / машинному навчанню, і ви можете отримати з нього ще якийсь код ...
CakeMaster

1
Я просто думав над тим, як записати обробку даних на простий блог Wordpress, просто щоб бути корисним для інших, і мені доведеться записати їх у будь-якому випадку. Мені подобаються всі ваші пропозиції, але я боюся, що мені доведеться чекати когось без терміну ;-)
Іво Фліпс

18

Використовуючи стійку гомологію для аналізу вашого набору даних, я отримую такий результат (натисніть для збільшення):

Результат

Це 2D-версія методу виявлення піку, описаного в цій відповіді SO . Наведений малюнок просто показує 0-мірні стійкі класи гомології, відсортовані за стійкістю.

Я збільшив масштаб оригінального набору даних на коефіцієнт 2 за допомогою scipy.misc.imresize (). Однак зауважте, що я розглядав чотири лапи як один набір даних; розділення його на чотири полегшило б проблему.

Методика. Ідея цього досить проста: Розгляньте графік функції функції, яка призначає кожному пікселю його рівень. Це виглядає приблизно так:

3D графік функцій

Тепер розглянемо рівень води на висоті 255, який постійно спускається до нижчих рівнів. На місцевих островах максимуми спливають (народження). У сідлових точках зливаються два острови; ми вважаємо нижній острів об'єднаним з вищим островом (смерть). Так звана діаграма персистенції (у класах гомології 0-го розміру, наші острови) зображує значення смерті над народжуваністю всіх островів:

Діаграма стійкості

наполегливість острова - це різниця між рівнем народжуваності та смерті; вертикальна відстань точки до сірої основної діагоналі. Фігура маркує острови, зменшуючи стійкість.

Найперша картина показує місця народження островів. Цей метод не тільки дає локальні максимуми, але і кількісно оцінює їх "значимість" вищезгаданою стійкістю. Тоді можна було б відфільтрувати всі острови із надто низькою стійкістю. Однак у вашому прикладі кожен острів (тобто кожен локальний максимум) - це пік, який ви шукаєте.

Код Python можна знайти тут .


16

Ця проблема була вивчена певною мірою фізиками. Є хороша реалізація в ROOT . Перегляньте класи TSpectrum (особливо TSpectrum2 для вашого випадку) та документацію для них.

Список літератури:

  1. М.Морхак та ін.: Фонові методи усунення для багатовимірних спектрів гамма-випромінювань збігу. Ядерні прилади та методи фізичних досліджень A 401 (1997) 113-132.
  2. М.Морхак та ін .: Ефективна одно- та двовимірна деконволюція золота та його застосування для розкладання гамма-спектрів. Ядерні прилади та методи фізичних досліджень A 401 (1997) 385-408.
  3. М.Морхак та ін .: Ідентифікація піків у спектрах багатовимірних збігів гамма-променів. Ядерні прилади та методи в дослідницькій фізиці A 443 (2000), 108-125.

... і для тих, хто не має доступу до підписки на NIM:


Для огляду на статтю, схоже, описується та сама обробка даних, що і я, але я боюся, що це значно перевершило мої навички програмування :(
Іво Фліпс

@Ivo: Я ніколи не намагався це реалізувати сам. Я просто використовую ROOT. Є прив'язки до python, але не забудьте знати, що ROOT - це досить важкий пакет.
dmckee --- колишнє кошеня-модератор

@Ivo Flipse: Я погоджуюся з dmckee. У вас є багато перспективних результатів в інших відповідях. Якщо всі вони провалюються, і вам здається, що ви інвестуєте якийсь час, ви можете заглибитися в ROOT, і він (мабуть) зробить все, що вам потрібно. Я ніколи не знав нікого, хто намагався навчитися ROOT через прив'язки python (а не природний C ++), тому бажаю удачі.
Physicsmichael

13

Ось ідея: ви обчислюєте (дискретний) лаплакійський образ. Я б очікував, що вона буде (негативною і) великою в максимумах, таким чином, що є більш драматичним, ніж у оригінальних образах. Таким чином, максими можна було легше знайти.

Ось ще одна ідея: якщо ви знаєте типовий розмір плям високого тиску, ви можете спочатку згладити своє зображення, з'єднавши його з гауссом такого ж розміру. Це може дати вам більш прості зображення для обробки.


11

Всього кілька ідей у ​​верхній частині моєї голови:

  • візьміть градієнт (похідне) сканування, подивіться, чи це усуває помилкові дзвінки
  • прийняти максимум локальних максимумів

Ви також можете поглянути на OpenCV , він має досить пристойний API Python і може мати деякі функції, які вам здадуться корисними.


Маючи на увазі градієнт, ви маєте на увазі, що я повинен обчислити крутизну схилів, як тільки це стане вище певного значення, я знаю, що "пік"? Я спробував це, але деякі пальці ніг мають дуже низькі вершини (1,2 Н / см) порівняно з іншими (8 Н / см). Тож як я повинен обробляти піки з дуже низьким градієнтом?
Іво Фліпс

2
Що працювало для мене в минулому, якщо я не міг використовувати градієнт безпосередньо, це дивитись на градієнт і максимуми, наприклад, якщо градієнт є локальною екстремумом, а я - локальним максимумом, то я перебуваю в точці відсотки.
ChrisC

11

Я впевнений, що у вас достатньо для продовження, але я не можу не запропонувати використовувати метод кластеризації k-означає. k-означає - алгоритм кластеризації без нагляду, який візьме вам дані (у будь-якій кількості вимірів - я трапляю це робити в 3D) і розташуватиме їх у k кластерах з чіткими межами. Тут приємно, бо ви точно знаєте, скільки пальців у цих іклів (повинні).

Крім того, він реалізований в Scipy, що дуже добре ( http://docs.scipy.org/doc/scipy/reference/cluster.vq.html ).

Ось приклад того, що можна зробити для просторового вирішення 3D-кластерів: введіть тут опис зображення

Те, що ви хочете зробити, є дещо іншим (2D і включає значення тиску), але я все одно думаю, що ви можете це зробити.


10

дякую за необроблені дані. Я в поїзді, і це наскільки я дістався (моя зупинка підходить). Я помасажував ваш файл txt регекзами і розмістив його на html-сторінці з деяким javascript для візуалізації. Я ділюсь цим сюди, тому що деякі, як я, можуть вважати його легше зламаним, ніж пітон.

Я думаю, що хорошим підходом буде інваріантність масштабу та обертання, і наступним моїм кроком буде дослідження сумішей гаусів. (кожна подушечка лап є центром гаусса).

    <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 текст


1
Я вважаю, що це є доказом концепції, що рекомендовані гауссові методи можуть спрацювати, коли б лише хтось міг довести це з Python ;-)
Іво Фліпс

8

Рішення фізика:
Визначте 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_i, alfaце параметр, який повинен бути максимально експериментальним)

Тепер час зробити деяку магію Метрополіс-Гастінгса:
1. Виберіть випадковий маркер і перемістіть його на один піксель у випадковому напрямку.
2. Обчисліть dE, різницю енергії, яку викликав цей хід.
3. Доберіть рівномірне випадкове число від 0-1 і назвіть його r.
4. Якщо dE<0або exp(-beta*dE)>r, прийняти хід і перейти до 1; якщо ні, скасуйте хід і перейдіть до 1.
Це потрібно повторити, поки маркери не сходяться на лапах. Бета-версія керує скануванням, щоб оптимізувати компроміс, тому його також слід оптимізувати експериментально; його також можна постійно збільшувати з часом моделювання (імітованого відпалу).


Хочете показати, як це могло б працювати на моєму прикладі? Оскільки я насправді не займаюся математикою високого рівня, тому мені вже важко
розгадувати

1
Це математика середньої школи, напевно, моє позначення просто заплутане. У мене є план його перевірити, тому слідкуйте за новинами.
mbq

4
Я фізик частинок. Протягом тривалого часу інструмент програмного забезпечення, що переходив до нашої дисципліни, називався PAW, і він мав сутність, пов'язану з графіками, що називається "маркер". Ви можете собі уявити, як заплутано я знайшов цю відповідь у перші пару разів ...
dmckee --- кошеня колишнього модератора

6

Ось ще один підхід, який я використовував, роблячи щось подібне для великого телескопа:

1) Пошук найвищого пікселя. Коли ви це зробите, пошукайте, щоб найкраще підібрати 2x2 (можливо, максимально збільшити суму 2х2), або зробіть 2d-гауссова підгонка всередині підрегіону скажімо, 4х4, орієнтована на найвищий піксель.

Потім встановіть ті 2х2 пікселі, які ви знайшли нульовими (а може бути, 3х3) навколо центру піку

поверніться до 1) і повторіть, поки найвища вершина не впаде нижче порогового рівня шуму або у вас є всі необхідні пальці


Хочете поділитися прикладом коду, який робить це? Я можу стежити за тим, що ви намагаєтеся зробити, але поняття не маю, як це
кодувати

Насправді я працюю з Matlab, так що так, це вже допоможе. Але якщо ви використовуєте дійсно чужі функції, мені може бути важко повторити це з Python
Ivo Flipse

6

Напевно, варто спробувати з нейронними мережами, якщо ви в змозі створити деякі дані тренувань ... але для цього потрібно багато зразків, анотованих вручну.


Якщо це варте клопоту, я б не проти зауважувати великий зразок вручну. Моя проблема полягала б у тому, як це зробити, оскільки я нічого не знаю про програмування нейронних мереж
Іво Фліпс

6

грубі контури ...

можливо, ви хочете використовувати алгоритм підключених компонентів для ізоляції кожної області лап. Вікі має гідний опис цього (з деяким кодом) тут: http://en.wikipedia.org/wiki/Connected_Component_Labeling

вам доведеться прийняти рішення про використання 4 або 8 підключення. особисто, для більшості проблем я віддаю перевагу 6-з'єднаності. у будь-якому разі, як тільки ви виділите кожен "друк лапи" як зв'язаний регіон, він повинен бути досить простим для перегляду регіону та пошуку максимумів. Після того, як ви знайдете максимуми, можете ітеративно збільшити область, поки не досягнете визначеного порогу, щоб визначити її як заданий "палець".

одна тонка проблема полягає в тому, що як тільки ви починаєте використовувати методи комп’ютерного зору, щоб визначити щось як праву / ліву / передню / задню лапу, і ви починаєте дивитися на окремі пальці ніг, ви повинні почати враховувати повороти, перекоси та переклади. це здійснюється за допомогою аналізу так званих "моментів". Є кілька різних моментів, які слід врахувати у програмах із баченням:

центральні моменти: переклад інваріант нормалізовані моменти: масштабування та переклад інваріантних моментів ху: переклад, масштаб та інтаріант обертання

Більше інформації про моменти можна знайти за допомогою пошуку "моментів зображення" на вікі.



4

Здається, ви можете трохи обдурити, використовуючи алгоритм jetxee. Перші три пальці він знаходить прекрасно, і ви зможете здогадатися, де на цьому базується четвертий.


4

Цікава проблема. Я б спробував таке рішення.

  1. Застосовуйте фільтр низьких частот, такий як згортка, з 2-ма гауссовою маскою. Це дасть вам купу значень (мабуть, але не обов'язково з плаваючою точкою).

  2. Виконайте 2D немаксимальне придушення, використовуючи відомий приблизний радіус кожної подушечки лапи (або пальця ноги).

Це повинно дати вам максимальні позиції, не маючи кількох кандидатів, які знаходяться близько один до одного. Просто для уточнення, радіус маски на етапі 1 також повинен бути аналогічним радіусу, який використовується на етапі 2. Цей радіус можна вибрати, або ветеринар може заздалегідь явно виміряти його (він буде змінюватися залежно від віку / породи / тощо).

Деякі із запропонованих рішень (середній зсув, нейронні мережі тощо), ймовірно, будуть працювати певною мірою, але надмірно складні і, ймовірно, не ідеальні.


У мене 0 досвід роботи з матрицями згортки та гауссовими фільтрами, тож ви хотіли б показати, як це буде працювати на моєму прикладі?
Іво Фліпс

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х2 квадрата та сортую його за сумою. Потім я беру 2х2 квадрат з найбільшою сумою за суперечкою, і кладу його вbest масив і видаляю всі інші 2х2 квадрати, які використовували будь-яку частину цього щойно видаленого 2х2 квадрата.

Здається, це добре працює, окрім останньої лапи (тієї, яка має найменшу суму в крайній правій частині вашої першої картинки), виявляється, що є два інших допустимих 2х2 квадрата з більшою сумою (і вони мають рівну суму до один одного). Один із них все ще вибирає один квадрат з вашого 2х2 квадрата, а другий - зліва. На щастя, на щастя, ми бачимо, що обираємо більше того, що хотіли б, але для цього, можливо, потрібні ще деякі ідеї, щоб отримувати те, чого ти насправді хочеш весь час.


Я вважаю, що ваші результати такі ж, як і у відповіді @ Jextee. Або, принаймні, так здається, я тестую це.
Іво Фліпсе


1

Можливо, тут достатньо наївного підходу: Створіть список усіх 2x2 квадратів на вашій площині, упорядкуйте їх за їх сумою (у порядку зменшення).

По-перше, виберіть квадрат з найбільшою цінністю у свій "список лап". Потім ітеративно виберіть 4 наступних найкращих квадрата, які не перетинаються з жодним із знайдених раніше квадратів.


Я фактично склав список з усіма сумами 2x2, але коли я їх замовляв, я не мав уявлення, як ітеративно їх порівняти. Моя проблема полягала в тому, що коли я сортував це, я втратив сліди координат. Можливо, я міг би вставити їх у словник із координатами як ключовими.
Іво Фліпс

Так, потрібен би якийсь словник. Я б припустив, що ваше представлення сітки - це вже якийсь словник.
Йоханнес Шарра

Добре, що зображення, яке ви бачите вище, - це масивний масив. Решта наразі зберігається у багатовимірних списках. Напевно, було б краще припинити це робити, хоча я не так знайомий з ітерацією над словниками
Іво Фліпс

1

У спільноті з астрономії та космології є декілька і обширних програм - це значна область досліджень як історично, так і в даний час.

Не турбуйтеся, якщо ви не астроном - деякі прості у використанні поза полем. Наприклад, ви можете використовувати astropy / photutils:

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

[Здається, тут повторюється короткий зразок коду. "

Неповний і трохи упереджений перелік методик / пакетів / посилань, які могли б зацікавити, наведено нижче - додайте більше в коментарі, і я оновлю цю відповідь за необхідності. Звичайно, існує компроміс точності та обчислення ресурсів. [Чесно кажучи, занадто багато, щоб наводити приклади коду в одній відповіді, такій як ця, тому я не впевнений, буде ця відповідь летіти чи ні.]

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

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

ASKAP / EMU виклик пошуку джерел: https://arxiv.org/abs/1509.03931

Ви також можете шукати проблеми з видобуванням джерел Planck та / або WMAP.

...


0

Що робити, якщо ви будете продовжувати крок за кроком: спочатку знаходите глобальний максимум, обробляєте, якщо потрібно, навколишні точки з урахуванням їх значення, потім встановлюєте знайдену область на нуль і повторюєте для наступної.


Гммм, це встановлення нуля принаймні видалило б його з будь-яких подальших розрахунків, це було б корисно.
Іво Фліпс

Замість того, щоб встановити нуль, ви можете обчислити гауссову функцію з вибраними вручну параметрами і відняти знайдені значення з вихідних показань тиску. Отже, якщо палець натискає на ваші датчики, знаходячи найвищу точку натискання, ви використовуєте його для зменшення впливу цього пальця на датчики, тим самим усуваючи сусідні комірки з великими значеннями тиску. en.wikipedia.org/wiki/File:Gaussian_2d.png
Даніяр

Хочете показати приклад на основі моїх зразкових даних @Daniyar? Оскільки я насправді не знайомий з таким видом обробки даних
Іво Фліпс

0

Я не впевнений, що це відповідає на питання, але здається, що ви можете просто шукати п ять найвищих вершин, у яких немає сусідів.

Ось суть. Зауважте, що це в 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 :-)
Іво Фліпс

Будь ласка, включіть код у саму публікацію, а не посилаючись на суть, якщо це розумна довжина.
agf
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.