5.25. Пошук плям

Порогова обробка перетворила захоплений кадр на бінарну маску: кожен піксель або проходить пороговий тест, або ні. Це відповідає на питання які кольори, що цікавлять програму, присутні в сцені, але не де — маска є лише морем з 1 і 0. Наступним кроком є виявлення плям: обхід маски, знаходження суміжних областей пікселів, що пройшли тест, і повернення кожної з них як об’єкта з позицією, розміром, орієнтацією та іншими властивостями, з якими програма може працювати.

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

5.25.1. Базовий виклик

find_blobs приймає список порогів і повертає список об’єктів результатів плям:

thresholds = [(30, 100, 15, 127, 15, 127)]  # LAB threshold for red
blobs = img.find_blobs(thresholds)

for b in blobs:
    img.draw_rectangle(b.rect, color=(255, 0, 0))
    img.draw_cross(b.cx, b.cy, color=(255, 0, 0))

Кожен кортеж порогу має ту саму форму, що й пороги, які передаються до binary() — шість елементів (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi) для зображення RGB565 (межі в LAB), два елементи (lo, hi) для зображення у відтінках сірого. В одному виклику можна передати до 32 порогів, що і робить find_blobs() настільки гнучким: червоні, зелені та сині маяки можна відстежувати одночасно, кожен вносить власні плями до поверненого списку, а властивість code кожної плями визначає, якому порогу вона відповідає.

Виклики draw_rectangle() і draw_cross() вище анотують захоплений кадр для попереднього перегляду в IDE. Результат плями вже містить b.rect (обмежувальний прямокутник у вигляді 4-кортежу) та b.cx / b.cy (цілочисельний центроїд), тому відображення виявлення на кадрі потребує лише двох викликів методів.

5.25.2. Що містить результат

Кожен Blob — це кортеж-атрибут, який об’єднує все, що детектор виміряв про область. Властивості поділяються на чотири групи.

Група обмежувального прямокутника та центроїдаx, y, w, h, rect, cx, cy, cxf, cyf — описує положення плями. rect — це 4-кортеж (x, y, w, h), який очікують методи малювання; cx і cy — центроїд у цілочисельних пікселях; cxf і cyf — центроїд у субпіксельних координатах з рухомою комою, корисний коли попередній етап калібрування потребує дрібних позицій.

Дескриптори форми* — pixels, area, density, perimeter, roundness, elongation, compactness, rotation — описують вигляд плями. pixels — кількість пікселів, що пройшли тест; area — площа вирівняного по осях обмежувального прямокутника (w * h); density — співвідношення цих двох значень, яке наближається до 1.0 для суцільного прямокутника і знижується до 0.0 для тонкої діагональної смуги. roundness і compactness обидва оцінюють округлість плями з різних геометричних точок зору (roundness — за моментами другого порядку, compactness — за співвідношенням периметра до площі); elongation — це 1.0 - roundness для зручності. rotation — орієнтація головної осі в радіанах, яка є найточнішою для витягнутих плям і стає шумною для майже круглих (неоднозначна вісь не має чітко визначеного напрямку).

Метадані порогу та злиття* — code, count — визначають, який поріг відповідає і скільки вихідних плям було об’єднано у повернений результат. code — це 32-бітна бітова карта з одним встановленим бітом на кожен відповідний поріг (один поріг дає code == 1; об’єднана багатокольорова пляма може мати кілька встановлених бітів); count дорівнює 1, якщо тільки merge=True не об’єднало кілька виявлень в одне.

Група кутів* — corners, min_corners — дає повернуту геометрію плями. corners — це 4-кортеж крайніх точок (x, y), витягнутих з контуру плями та відсортованих за годинниковою стрілкою починаючи з верхнього лівого кута; min_corners — це 4-кортеж кутів повернутого прямокутника мінімальної площі, що охоплює пляму. Прямокутник мінімальної площі є щільним обхватом; вирівняний по осях rect є вільним обхватом, вирівняним із сіткою пікселів. Обидва є корисними залежно від того, чи потребує наступний етап орієнтованого або звичайного прямокутника.

A blob detection illustrated against a binary threshold mask. The left panel shows a tilted oval mask of passing pixels. The right panel shows the same mask annotated with the axis-aligned bounding box drawn around it, the centroid marked with a cross in the middle, a dashed minimum-area rotated rectangle hugging the oval at its true angle, and the major-axis line through the centroid pointing along the oval's long direction.

Пляма містить вирівняний по осях обмежувальний прямокутник (rect, x, y, w, h), центроїд (cx, cy або субпіксельний cxf, cyf), повернутий прямокутник мінімальної площі (min_corners плюс rotation), а також необов’язкові лінії головної / малої осі, обчислені допоміжними функціями рівня модуля нижче.

5.25.4. Злиття перекриваючихся плям

merge=True після обробки об’єднує результуючий список для поєднання плям, чиї обмежувальні прямокутники перекриваються. Природне використання — виявлення цілі, колір якої камера бачить як кілька порогових областей через дзеркальні блиски, тіньові лінії або невідповідне освітлення по всьому об’єкту: один червоний м’яч може повернутися як три або чотири малі червоні плями, які разом окреслюють м’яч. З merge=True три плями стають однією великою плямою, rect охоплює об’єднання, code є порозрядним АБО кодів об’єднаних плям (тому багатокольорове злиття визначає, які кольори брали участь), а count повідомляє, скільки вихідних плям було об’єднано.

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

merge_cb виконується для кожної пари-кандидата до того, як відбувається злиття. Зворотний виклик отримує дві плями і повертає True, щоб дозволити злиття, або False, щоб запобігти йому. Це правильний інструмент для перехресної перевірки злиттів, які геометричне правило пропускає — наприклад, відмова об’єднувати дві плями, кути rotation яких відрізняються більш ніж на поріг, або відмова об’єднувати маленьку пляму з набагато більшою, якщо маленька є лише шумом.

5.25.5. Проекційні гістограми

x_hist_bins_max і y_hist_bins_max прикріплюють необов’язкові проекційні гістограми до кожної плями. Проекційна гістограма — це кількість пікселів, що пройшли тест, вздовж однієї осі: гістограма по осі X підсумовує пікселі, що пройшли тест, по стовпцях всередині обмежувального прямокутника плями, а гістограма по осі Y — по рядках. Обидва значення за замовчуванням рівні нулю — гістограми не обчислюються, якщо не вказано ненульове значення max, оскільки в іншому випадку вони додавали б роботу до кожного виявлення.

Коли вони обчислені, гістограми надають дешевий одновимірний сигнал, на якому програма може проводити подальший аналіз: виявлення позиції вертикальної смуги всередині плями, пошук точки розриву двокольорової цілі, підрахунок кількості пропусків вздовж довгої осі. Вони заповнюються як властивості x_hist_bins і y_hist_bins для кожного Blob.

5.25.6. Додаткові геометричні допоміжні засоби

Кілька додаткових геометричних вимірювань існують як функції рівня модуля, що приймають пляму і повертають запитуване вимірювання:

  • image.get_solidity() повертає суцільність плями — пікселі, поділені на площу опуклої оболонки. Суцільна заповнена область близька до 1.0; пляма з увігнутостями (підкова, рука з розведеними пальцями) значно нижча.

  • image.get_convexity() повертає опуклість — периметр опуклої оболонки, поділений на периметр плями. Ідеально опукла пляма дає 1.0; нерівні або з виїмками плями мають нижчі значення.

  • image.get_major_axis_line() і image.get_minor_axis_line() повертають об’єкти Line вздовж головної та малої осей плями, виведені з повернутого прямокутника мінімальної площі.

  • image.get_enclosing_circle() повертає Circle, що охоплює пляму — корисний, коли наступний етап потребує кола для малювання або перевірки.

  • image.get_enclosed_ellipse() повертає 5-кортеж (cx, cy, rx, ry, rotation) для еліпса, вписаного в прямокутник мінімальної площі плями. Значення безпосередньо передаються у draw_ellipse().

5.25.7. Автоматичне навчання порогу

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

Перший — інтерактивний вибір в IDE: захопіть кадр, виділіть прямокутником приклад цільового кольору і дозвольте редактору порогів в IDE повідомити межі LAB, які він бачить. Ці межі вставляються в скрипт як пороги find_blobs(), і детектор готовий.

Другий — програмне автоматичне навчання: процедура калібрування, що виконується на камері, захоплює кадр, будує гістограму відомої ділянки, де знаходиться ціль (get_histogram() з roi=), і зчитує діапазон значень ділянки з гістограми за допомогою get_percentile(). 5-й перцентиль встановлює нижню межу кожного каналу, а 95-й — верхню, ігноруючи випадкові пікселі-викиди на обох кінцях. На зображенні RGB565 один виклик перцентиля повідомляє всі три канали LAB одночасно, тому два виклики дають шість чисел, яких очікує find_blobs():

h = img.get_histogram(roi=patch)
lo = h.get_percentile(0.05)
hi = h.get_percentile(0.95)
threshold = (lo.l_value, hi.l_value,
             lo.a_value, hi.a_value,
             lo.b_value, hi.b_value)