5.27. Пошук кіл і прямокутників#
Лінії та відрізки охоплюють прямі краї у захопленому кадрі, але багато реальних об’єктів, які шукає камера, не є прямолінійними. Монета, що лежить на столі, — це коло. Надрукована етикетка, стікер, верхня частина коробки, побачена під кутом — це чотирикутник. Модуль зображень надає спеціальний детектор для кожного: пошук кіл у стилі Хафа та пошук чотирикутників, заснований на AprilTag.
Обидва методи слідують тому самому шаблону, що й детектори ліній: threshold контролює кількість голосів, необхідних для виявлення, roi звужує область пошуку, а повернуті об’єкти містять як позицію, так і значення впевненості. Однак простори параметрів і правильні значення за замовчуванням відрізняються настільки, що заслуговують окремого розгляду.
5.27.1. Кола Хафа#
find_circles() виконує кільцевий варіант перетворення Хафа. Кожен піксель межі, виявлений під час попереднього проходу Собеля, голосує за кожне коло, що може через нього проходити; кола, які набирають достатню кількість голосів, повертаються як результат.
circles = img.find_circles(threshold=3500,
x_margin=10, y_margin=10, r_margin=10,
r_min=10, r_max=80, r_step=2)
for c in circles:
img.draw_circle((c.x, c.y, c.r), color=(255, 0, 0))
threshold — це мінімальна сума магнітуд меж Собеля вздовж кандидата-кола. Більші кола проходять через більше пікселів і тому потребують вищих порогів; значення, що знаходить монету радіусом 20 пікселів, також спрацює на шум навколо межі в 100 пікселів, тоді як значення, налаштоване на велику монету, пропустить маленьку. Коли діапазон очікуваних радіусів відомий, правильне значення порогу масштабується з довжиною кола — приблизно threshold = 50 * 2 * pi * r дає розумну відправну точку, а точне значення визначається в ході короткого налаштування.
r_min, r_max і r_step задають пошук за радіусом. Без обмежень детектор шукатиме кожен радіус від кількох пікселів до половини ширини зображення, що є і повільним, і джерелом хибних спрацювань. Встановлення r_min і r_max для охоплення очікуваного розміру цілі з достатнім запасом (наприклад, r_min=15, r_max=25 для монети, радіус якої становить близько 20 пікселів) значно скорочує обсяг роботи та покращує співвідношення сигнал-шум голосів. r_step контролює гранулярність пошуку; більші кроки прискорюють роботу, але можуть пропустити коло, справжній радіус якого потрапляє між двома перевіреними значеннями. Значення за замовчуванням r_step=2 є розумним компромісом.
x_margin, y_margin і r_margin контролюють об’єднання близьких виявлень — так само, як theta_margin і rho_margin роблять це для виявлення ліній. Одне фізичне коло на зображенні голосує за кластер кіл-кандидатів, центри та радіуси яких збігаються з точністю до кількох пікселів; поля стягують кожен кластер до його піку перед побудовою списку результатів. Більші поля повертають менше, але впевненіших виявлень; менші поля повертають більше виявлень з можливими майже-дублікатами.
x_stride і y_stride задають крок сканування при голосуванні — так само, як в інших детекторах. Значення за замовчуванням 2 і 1 підходять для більшості зображень; збільшення обох до 4 — стандартний компроміс між швидкістю та якістю для зображень з відомо великими колами.
Кожен повернутий Circle містить x, y, r (центр і радіус) та magnitude (загальна кількість голосів, корисна як оцінка впевненості для сортування або фільтрації). Відображення виявлення у кадрі — це один виклик: draw_circle() приймає кортеж (x, y, r) з 3 елементів, доступний безпосередньо як (c.x, c.y, c.r) з результату.
5.27.2. Прямокутники#
find_rects() запозичує детектор чотирикутників з конвеєра AprilTag — та сама процедура, що знаходить чорний квадрат навколо тегу, тут використовується самостійно як універсальний детектор прямокутників.
rects = img.find_rects(threshold=12000)
for r in rects:
img.draw_rectangle(r.rect, color=(0, 255, 0))
for corner in r.corners:
img.draw_circle((corner[0], corner[1], 4),
color=(0, 255, 0))
threshold — це мінімальна сума магнітуд меж навколо периметра прямокутника. Друкований чорно-білий прямокутник у добре освітленому кадрі легко перевищує 10000; слабко видимий прямокутник на текстурованому фоні може вимагати зниження до 2000 — це компроміс між хибними спрацюваннями та чутливістю. Як і у детектора кіл, правильне значення визначається в ході швидкого налаштування з потрібними цілями в кадрі.
Детектор є проективним — він знаходить чотирикутники, сторони яких прямі, але не обов’язково паралельні або вирівняні за осями. Наклейка, знята під кутом, виглядає на зображенні як трапеція, і детектор прямокутників правильно її знаходить; вирівняний за осями прямокутник — це просто вироджений випадок, де чотири кути утворюють прямокутну коробку. roi обмежує пошук; решта іменованих аргументів успадковують значення за замовчуванням з конвеєра AprilTag і рідко потребують налаштування.
Кожен повернутий Rect містить обмежувальний прямокутник, вирівняний за осями: x, y, w, h, а також кортеж rect з 4 елементів, який приймає draw_rectangle(), і чотири виявлені кути як corners. Обмежувальний прямокутник використовується застосунком для приблизного визначення позиції та розміру; кути описують сам проективний чотирикутник. Коли камера знімає плоску ціль під кутом і застосунку потрібно усунути трапецієподібне спотворення — зчитати текст із наклейки, взяти зразок кольору з плоскої ділянки — кути подаються безпосередньо до rotation_corr() з ключовим словом corners= (див. виправлення лінзи та перспективи), і результатом є виправлений прямокутник, готовий до подальшого аналізу.
Попередження
Оскільки детектор налаштований під потреби конвеєра AprilTag — чотирикутники з чіткими, контрастними межами, як-от контур чорного тегу на білому папері — він не є засобом пошуку будь-яких прямокутників. Прямокутники з низьким контрастом, текстурованими межами або на насиченому тлі можуть залишитися невиявленими. Ефективність роботи залежить від ситуації: перевіряйте результати на реальних цілях завчасно, перш ніж будувати навколо цього конвеєр.
5.27.3. Коли детектор спрацьовує помилково#
Кола особливо виграють від попередньої фільтрації вхідного зображення. Зашумлений кадр містить багато випадкових пікселів меж, які голосують, і простір Хафа має широкі розмиті піки, які важко розділити при об’єднанні. Прохід gaussian() або mean() перед find_circles() згладжує шум і зберігає справжні межі; детектор повертає чіткіші піки за менший час.
Для прямокутників поширений збій є протилежним: низький контраст між прямокутником і його фоном означає, що сума магнітуд меж ніколи не досягає threshold. Прохід histeq() для перерозподілу яскравості по всьому діапазону від 0 до 255 відновлює контраст, необхідний детектору. (Контраст має існувати десь у зображенні; вирівнювання гістограми може лише посилити те, що вже є.)