Пошук малюнка, що нагадує зебру в зображенні (Виявлення центральної лінії бахроми з фотографії)


12

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

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

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

Нижче наведено фактичне зразкове зображення:

введіть тут опис зображення

Будь-яка пропозиція буде дуже вдячна!


це дуже цікаво. Але, до речі, я роблю деякі дослідження, використовуючи кольорову смужку для виявлення 3d-об’єкта. Оскільки за допомогою кольорової смуги легко знайти відповідність кожної смуги від проектора. Тому за допомогою тригонометрії можна обчислити 3d-інформацію. Як ви знайдете відповідність, якщо колір однаковий? Я думаю, ваш проект також стосується 3d реконструкції?

@johnyoung: Будь ласка, не додайте коментарів як відповіді. Я усвідомлюю, що вам потрібна репутація, перш ніж ви зможете коментувати, але будь ласка, утримайтеся від свого поточного курсу дій. Я пропоную задавати власні (споріднені) запитання або відповідати на питання інших, щоб збільшити кількість представників.
Пітер К.

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

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

Відповіді:


13

Я пропоную наступні кроки:

  1. Знайдіть поріг, щоб відокремити передній план від фону.
  2. Для кожної краплі у двійковому зображенні (одна зеброва смуга) для кожного xзнайдіть зважений центр (за інтенсивністю пікселів) у yнапрямку.
  3. Можливо, згладьте yзначення, щоб видалити шум.
  4. З’єднайте (x,y)точки, встановивши якусь криву. Ця стаття може вам допомогти. Ви також можете помістити поліном високого рівня, хоча це, на мою думку, і гірше.

Ось код Matlab, який показує кроки 1,2 та 4. Я пропустив автоматичний вибір порогу. Натомість я вибрав посібник th=40:

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

Це криві після встановлення многочлена: введіть тут опис зображення

Ось код:

function Zebra()
    im = imread('http://i.stack.imgur.com/m0sy7.png');
    im = uint8(mean(im,3));

    th = 40;
    imBinary = im>th;
    imBinary = imclose(imBinary,strel('disk',2));
    % figure;imshow(imBinary);
    labels = logical(imBinary);
    props =regionprops(labels,im,'Image','Area','BoundingBox');

    figure(1);imshow(im .* uint8(imBinary));
    figure(2);imshow(im .* uint8(imBinary));

    for i=1:numel(props)
        %Ignore small ones
        if props(i).Area < 10
            continue
        end
        %Find weighted centroids
        boundingBox = props(i).BoundingBox;
        ul = boundingBox(1:2)+0.5;
        wh = boundingBox(3:4);
        clipped = im( ul(2): (ul(2)+wh(2)-1), ul(1): (ul(1)+wh(1)-1) );
        imClip = double(props(i).Image) .* double(clipped);
        rows = transpose( 1:size(imClip,1) );
        %Weighted calculation
        weightedRows  = sum(bsxfun(@times, imClip, rows),1) ./ sum(imClip,1);
        %Calculate x,y
        x = ( 1:numel(weightedRows) ) + ul(1) - 1;
        y = ( weightedRows ) + ul(2) - 1;
        figure(1);
        hold on;plot(x,y,'b','LineWidth',2);
        try %#ok<TRYNC>
            figure(2);
            [xo,yo] = FitCurveByPolynom(x,y);
            hold on;plot(xo,yo,'g','LineWidth',2);
        end
        linkaxes( cell2mat(get(get(0,'Children'),'Children')) )
    end        
end

function [xo,yo] = FitCurveByPolynom(x,y)
   p = polyfit(x,y,15); 
   yo = polyval(p,x);
   xo = x;
end

Мені це здалося дуже цікавим. Я використовую Python, але все одно мені доведеться вивчити обґрунтування всього цього. Як незалежний коментар, я схильний не виконувати класичну обробку зображень (безпосередньо на квантованих контейнерах зображень, таких як масиви uint8), а замість цього завантажую все в пам'ять як плавні масиви перед застосуванням операцій. Також я здивований результатами нижньої половини вашого зображення, сині лінії не проходять по очікуваній середній лінії бахроми ... (?). Дякую зараз, я отримаю відгуки, як тільки отримаю якийсь результат!
heltonbiker

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

1
@heltonbiker, готово. Це справді помилка, пов’язана з індексуванням на основі 1.
Андрій Рубштейн

Відмінник! Дивовижно, справді. Завдяки цій техніці, і в моїх цілях, згладжування не тільки навіть не знадобиться, але й буде шкідливим. Велике спасибі за ваш інтерес!
heltonbiker

3

Я б не використовував RGB-зображення. Кольорові зображення, як правило, робляться, якщо на датчик камери розміщується "фільтр Байєра" , що зазвичай зменшує роздільну здатність, яку ви можете досягти.

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

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

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

@heltonbiker - як швидкий тест використовуйте лише зелений канал. На кольоровому датчику зазвичай в 2 рази більше зелених пікселів, і він менш інтерполізований, ніж червоний і синій
Мартін Бекетт

@MartinBeckett Дякую за ваш інтерес, я вже проаналізував кожен канал, і справді зелений, здається, набагато більш вирішений, ніж, скажімо, червоний. Значення інтенсивності накреслення вертикальних перерізів для кожного каналу, однак, "смугастий малюнок", схоже, не так сильно змінюється між каналами, і я наразі їх змішую порівну при перетворенні в сірий масштаб. Незважаючи на те, що я все ще планую вивчити найкращу лінійну комбінацію між каналами, щоб отримати найкращий контрастний результат, АБО для отримання зображень, які вже є в градаціях сірого. Знову дякую!
heltonbiker

3

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

З дуже високого рівня ми повинні розглядати це зображення як графік, де

  1. кожен піксель зображення - це вузол на цьому графіку

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

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

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

Ось деякі плюси використання цього підходу:

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

  2. дуже багато свобод для побудови такого графіка, ви можете вибрати різні функції та топологію графа.

  3. ваші результати оптимальні в сенсі оптимізації шляху

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

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

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

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

Можна відновити часткові шляхи, змінивши початкові та кінцеві вузли.

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

Для отримання більш детальної інформації ви можете звернутися до статті.

 Wu, Y.; Zha, S.; Cao, H.; Liu, D., & Natarajan, P.  (2014, February). A Markov Chain Line Segmentation Method for Text Recognition. In IS&T/SPIE 26th Annual Symposium on Electronic Imaging (DRR), pp. 90210C-90210C.

Ось короткий фрагмент пітонного коду, який використовується для створення вищевказаного графіка.


import cv2
import numpy as np
from matplotlib import pyplot
# define your image path
image_path = ;
# read in an image
img = cv2.imread( image_path, 0 );
rgb = cv2.imread( image_path, -1 );

# some feature to reflect how likely a node is in an optimal path
img = cv2.equalizeHist( img ); # equalization
img = img - img.mean(); # substract DC
img_pmax = img.max(); # get brightest intensity
img_nmin = img.min(); # get darkest intensity
# express our preknowledge
img[ img > 0 ] *= +1.0  / img_pmax; 
img[ img = 1 :
    prev_idx = vt_path[ -1 ].astype('int');
    vt_path.append( path_buffer[ prev_idx, time ] );
    time -= 1;
vt_path.reverse();    
vt_path = np.asarray( vt_path ).T;

# plot found optimal paths for every 7 of them
pyplot.imshow( rgb, 'jet' ),
for row in range( 0, h, 7 ) :
    pyplot.hold(True), pyplot.plot( vt_path[row,:], c=np.random.rand(3,1), lw = 2 );
pyplot.xlim( ( 0, w ) );
pyplot.ylim( ( h, 0 ) );

Це дуже цікавий підхід. Зізнаюсь, тема "графіки" була для мене незрозумілою до недавнього часу, коли (у цьому ж проекті) я міг лише вирішити ще одну проблему за допомогою графіків. Після того, як я "отримав це", я зрозумів, наскільки потужними можуть бути ці найкоротші алгоритми шляхів. Ваша ідея дуже цікава, і не неможливо, що я б доповнив цю ідею, якщо маю потребу / можливість. Дуже дякую.
heltonbiker

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

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

3

Думав, я повинен розмістити свою відповідь, оскільки вона трохи відрізняється від інших підходів. Я спробував це в Matlab.

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

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

Ось код Matlab:

im = imread('m0sy7.png');
imsum = sum(im, 3); % sum all channels
h = fspecial('gaussian', 3);
im2 = imclose(imsum, ones(3)); % close
im2 = imfilter(im2, h); % smooth
% for each column, find regional max
mx = zeros(size(im2));
for c = 1:size(im2, 2)
    mx(:, c) = imregionalmax(im2(:, c));
end
% find connected components
ccomp = bwlabel(mx);

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

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

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

введіть тут опис зображення


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