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 є вільним обхватом, вирівняним із сіткою пікселів. Обидва є корисними залежно від того, чи потребує наступний етап орієнтованого або звичайного прямокутника.
Пляма містить вирівняний по осях обмежувальний прямокутник (rect, x, y, w, h), центроїд (cx, cy або субпіксельний cxf, cyf), повернутий прямокутник мінімальної площі (min_corners плюс rotation), а також необов’язкові лінії головної / малої осі, обчислені допоміжними функціями рівня модуля нижче.¶
5.25.3. Фільтрація пошуку¶
Захоплений кадр зазвичай містить пікселі, що відповідають порогу з причин, відмінних від об’єкта, який цікавить програму: дзеркальні блиски, далекі фонові об’єкти, пікселі шуму зображення, що випадково потрапляють у діапазон LAB. Аргументи-ключові слова до find_blobs() є першою лінією захисту.
roi обмежує пошук областю кадру, як це робить кожен інший метод модуля зображення. Програма, що знає, що об’єкт може з’явитися лише в нижній половині поля зору, передає roi=(0, h//2, w, h//2) і ігнорує все вище; зекономлений час повертається у швидкість кадрів.
area_threshold і pixels_threshold обидва фільтрують плями, занадто малі щоб їх враховувати. area_threshold відкидає плями, чий обмежувальний прямокутник має менше вказаної кількості пікселів площі (добре для фільтрації розсіяного шуму); pixels_threshold відкидає плями, що мають менше вказаної кількості пікселів, що пройшли тест (добре для фільтрації плям, що є великими, але розрідженими, наприклад, порогова обробка пунктирного малюнку, де лише один або два пікселі відповідають тут і там). Обидва значення за замовчуванням рівні 10; збільшення їх до сотень для цільового об’єкта переднього плану на відстані кількох сантиметрів відкидає кожну крапку дрібного шуму.
x_stride і y_stride встановлюють кроки пікселів, які сканер робить при пошуку плями для початку трасування. Крок — це не роздільна здатність трасування — трасування завжди слідує реальній межі плями з детальністю до одного пікселя — але він контролює, наскільки швидко сканування знаходить початковий піксель. Коли плями відомо великі (кольорова ціль розміром з кулак за метр від камери, легко понад сотню пікселів у поперечнику), x_stride=4, y_stride=4 скорочує час сканування у шістнадцять разів без практичних втрат у виявленні. Коли плями малі (далекий маяк-світлодіод, кілька пікселів у поперечнику), кроки повинні залишатися на 1, щоб уникнути повного пропуску їх. invert перевертає пороговий тест: відповідність стає невідповідністю, і процедура повертає плями пікселів, що не пройшли тест.
threshold_cb — це зворотний виклик Python, що викликається для кожної плями після порогової обробки, але до формування остаточного списку результатів. Зворотний виклик отримує пляму і повертає True, щоб зберегти її, або False, щоб відкинути. Це місце для застосування довільних фільтрів рівня Python до властивостей, які аргументи-ключові слова не розкривають безпосередньо — мінімальна щільність, певний діапазон обертання, спеціальна комбінація бітів коду після злиття. Аргументи-ключові слова є фільтрами у рідному коді і виконуються швидко; зворотний виклик виконується в Python і є повільнішим, але необмеженим у тому, що він може виражати.
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)