У попередній темі ми зробили згортку руками й дійшли до Собеля й Кенні: з картинки виходить інша картинка, де світяться межі. Це вже не сира яскравість, але це досі зображення — тисячі чисел. А для більшості задач потрібно щось інше: коротке число, за яким два знімки можна порівняти. Це і зветься ознакою (feature), і вся ця тема — про те, як її вигадували руками.
Слово «вигадували» тут не образа. Придумані ознаки зробили те, що працює й досі: панорами в телефоні, стабілізація відео, навігація робота по камері. Але той самий підхід програв у розпізнаванні, і програв нищівно. Ця тема — про обидві половини історії, бо без другої не зрозуміти, навіщо знадобився наступний блок. Сцена та сама, що в темах 01 і 02: «фото товару з дошки оголошень», телефон на столі, на екрані сітка іконок, і ми його малюємо формулами.
ЗадачаЧому не можна порівняти пікселі
Здається, що порівняти два зображення просто: відніми одне від одного й подивись, чи близький результат до нуля. Спробуй — і воно розсиплеться на першому ж прикладі. Візьми знімок і зсунь його на один піксель праворуч. Око не побачить різниці взагалі. А порівняння піксель у піксель побачить: на кожній межі, де яскравість стрибала зі 60 на 230, тепер стоїть чуже значення, і різниця там 170 рівнів. Формально знімки «різні» — настільки ж різні, наскільки два зовсім різні фото.
Далі ще гірше. Ввімкнули лампу яскравіше — усі числа поїхали вгору, різниця величезна, предмет той самий. Повернули телефон на пʼять градусів — так само. Відійшли на крок далі — знову. Пряме порівняння реагує на все, крім того єдиного, що нас цікавить: чи це той самий предмет.
Звідси задача теми. Потрібне перетворення, яке з масиву на 20 тисяч чисел робить короткий вектор — скажімо, на 32 або на 128 чисел — так, щоб у нього не потрапила дрібниця, а потрапила суть. Цей короткий вектор і зветься ознакою, або дескриптором (descriptor), а перетворення — виділенням ознак (feature extraction).
МірилоТри вимоги до ознаки
Перш ніж вигадувати ознаки, домовимось, чим їх міряти. Вимог рівно три, і далі кожна ознака в темі складатиме іспит саме за цими трьома.
| Вимога | Що означає | Як перевіряємо |
|---|---|---|
| Стійкість | Спотворили знімок — вектор майже не змінився. Спотворення: зсув, поворот, масштаб, освітлення, шум. | Порахувати вектор до і після й поміряти, наскільки вони близькі |
| Розрізняльність | Два різні предмети дають різні вектори. Ознака, стійка до всього на світі, стійка й до зміни предмета — і тому нікчемна. | Порахувати вектор для іншого предмета й переконатись, що він далеко |
| Компактність | Вектор коротший за саме зображення: місце, час порівняння й памʼять залежать від його довжини. | Просто подивитись на довжину |
Головне про ці три вимоги: вони тягнуть у різні боки. Найстійкіша ознака у світі — константа: вона однакова для будь-якого зображення, а отже, нічого не розрізняє. Найрозрізняльніша — саме зображення: унікальне, але не стійке ні до чого й не коротке. Уся історія придуманих ознак — це пошук точки між цими крайнощами.
Як міряти «близькість» двох векторів, домовимось теж один раз: перемнож їх поелементно, додай добутки й поділи на добуток довжин самих векторів. Виходить число від −1 до 1: 1 — вектори дивляться в один бік (збіг), 0 — нічого спільного. Це косинусна близькість (cosine similarity), і саме її показують табло в інтерактивах нижче. Мінус одиниця означала б «дивляться в протилежні боки», але в цій темі вона не трапиться: усі наші вектори — це підрахунки, тобто невідʼємні числа, і нижче нуля косинус між ними не спускається.
ГістограмаГістограма яскравості
Найпростіша ознака, яку взагалі можна вигадати: полічити, скільки в зображенні пікселів кожної яскравості. Отримаємо 256 чисел — по одному на кожен рівень від 0 до 255. Це гістограма, і ми вже бачили її в темі 01.
Три вимоги вона складає нерівномірно. Компактність чудова: 20 480 чисел стиснулись у 256, а якщо групувати по 8 рівнів у корзину (bin), то й у 32. Стійкість теж чудова — і саме тут пастка. Гістограма стійка до зсуву, повороту, дзеркального відбиття й навіть до повного перемішування пікселів, бо взагалі не знає, де саме кожен піксель лежав: вона рахує тільки, скільки їх якого відтінку.
Що саме викидає гістограма
Угорі — «фото» після перетворення. Унизу — його гістограма, накладена на гістограму оригіналу. Перемкни перетворення й подивись, чи стовпчики зрушили хоч на волосину.
Це не спостереження, а наслідок означення, і доводиться він в один
рядок. Гістограма відповідає рівно на одне питання: «скільки пікселів мають значення
v». А переворот, дзеркальне відбиття, циклічний зсув і навіть
повне перемішування — це перестановки: пікселі міняються місцями, але жоден не
зникає, не додається й не міняє власного значення. Отже, скільки пікселів мали значення
v до перестановки, стільки й після — для кожного з 256 значень
одразу. Гістограма зобовʼязана збігтися, і не «майже», а побітово: це арифметика, а не
щасливий збіг. Перевіряється воно одним
np.array_equal(cv2.calcHist(a,…), cv2.calcHist(b,…)), і відповідь буде
True для кожної з чотирьох операцій.
А от притемнення — не перестановка: воно міняє самі значення, а не місця пікселів. Тому саме воно — і тільки воно — гістограму й ламає. Ознака, яку не чіпає перемішування картинки в кашу, але валить поворот регулятора яскравості: неважко уявити гіршу пару властивостей для впізнавання предметів.
Отже, розрізняльність слабка, а ламається гістограма там, де людина ламатись не чекає —
від зміни освітлення. Тому в реальних системах беруть гістограму не яскравості, а
тону: переводять знімок у HSV і будують гістограму по каналах H і S,
викидаючи канал V, який і є яскравістю. Порівнює їх cv2.compareHist, і
на нашій сцені після множення яскравості на 0.6 виходить близькість 0.999 — світло
таку гістограму майже не турбує. Але перевернутий згори вниз кадр дає 1.000:
простору вона все одно не бачить, лише перестала боятись лампи.
І тут же чесна межа, бо вона є. Там, де треба дешево відкинути завідомо не те, гістограма й сьогодні найкращий вибір — і жодна мережа її звідти не витіснила. Причина в ціні: 32 числа проти 3780 у HOG і 512 байтів у SIFT, а порівняння двох гістограм — це 32 множення, тобто мільйони порівнянь за секунду на звичайному процесорі, без відеокарти й без моделі. Тому гістограму ставлять перед дорогою частиною: відсіяти кадри відео, у яких нічого не змінилось; знайти дублікати у великій теці знімків; звузити мільйон кандидатів до тисячі, яку вже можна віддати чомусь розумнішому. Правило просте: гістограма не годиться, щоб відповісти «що це», і чудово годиться, щоб відповісти «це точно не воно».
ПростірКуди дівається простір
Слабкість гістограми названа точно: вона викинула координати. Напрошується перше компромісне рішення, і воно справді так і робиться. Розріж зображення сіткою на клітинки і візьми гістограму окремо в кожній клітинці, а потім склей їх в один довгий вектор: тепер він знає не тільки «скільки темного», а й «темне було в лівому нижньому куті». Це просторова піраміда (spatial pyramid).
Ціна очевидна з арифметики. Візьмімо для рахунку грубішу гістограму — не 256 корзин, а 16, по 16 рівнів яскравості в кожній: саме її і рахує інтерактив нижче, бо з 256 корзинами на клітинку вектор роздувся б до нечитабельного. Було 16 корзин на весь знімок; стало 16 корзин на кожну з n × n клітинок, тобто при сітці 8 × 8 — 1024 числа, у 64 рази довше. І зʼявилась нова слабкість: якщо предмет зсунеться на пів клітинки, гістограми поїдуть у сусідні клітинки, і вектор зміниться.
Просторова піраміда
Повзунок ділить кадр сіткою. Ліворуч — клітинки на «фото», праворуч — довжина вектора й дві близькості: до зсунутого кадру (стійкість) і до перевернутого (розрізняльність).
Запамʼятай форму цього компромісу: щоб ознака почала розрізняти, у неї довелося повернути частину просторової інформації, і рівно настільки вона стала крихкішою. Далі ми повторимо цей хід ще двічі — у HOG і в SIFT.
ГрадієнтиГрадієнти замість яскравості
Гістограму ламає освітлення. Виправлення підказує попередня тема: рахувати не яскравість, а градієнт — на скільки яскравість змінюється від пікселя до сусіда. Собель із теми 04 дає gx (зміна вправо) і gy (зміна вниз), а з них — модуль і напрямок.
Словами: модуль — наскільки різко змінюється яскравість у цій точці, тобто «наскільки
тут межа». Напрямок — куди саме вона змінюється найшвидше, тобто під яким кутом ця межа
лежить. Символ atan2 — це функція, яка з двох катетів прямокутного
трикутника повертає кут; вона є в кожній мові й у NumPy зветься np.arctan2.
Розберемо запис по знаках. gx — наскільки яскравість зростає, якщо ступити на піксель управо; gy — те саме вниз. Обидва можуть бути відʼємні: це означає «спадає». Дві риски |g| — це довжина, а корінь із суми квадратів тут не з примхи: gx і gy — два катети прямокутного трикутника, а модуль градієнта — його гіпотенуза, тобто це теорема Піфагора й нічого понад неї. Верхня двійка означає «помножити число саме на себе».
Порахуймо руками, щоб побачити механізм. Хай у якійсь точці gx = 3, а gy = 4. Тоді |g| = √(3² + 4²) = √(9 + 16) = √25 = 5, а напрямок = atan2(4, 3) = 53.1°. Тепер пригасімо лампу вдвічі — обидва числа діляться навпіл: gx = 1.5, gy = 2. Модуль стає √(2.25 + 4) = √6.25 = 2.5, тобто теж удвічі менший. А напрямок — atan2(2, 1.5) = ті самі 53.1°. Ось уся ідея на пʼяти числах: модуль поїхав, напрямок не зрушив.
Тепер найважливіше — чому це рятує від освітлення. Світло на знімку змінюється двома типовими способами.
До кожного пікселя додалося одне й те саме число: було 100 і 160, стало 140 і 200. Різниця сусідів була 60 і лишилась 60 — доданок скоротився при відніманні. Градієнт не змінився взагалі, ні модуль, ні напрямок.
Кожен піксель помножився на 0.5: було 100 і 160, стало 50 і 80. Різниця була 60, стала 30 — модуль градієнта помножився на ті самі 0.5. Але gx і gy помножились на 0.5 обидва, а кут залежить лише від їхнього відношення. Відношення не змінилось — напрямок лишився той самий до останнього знака.
Один-єдиний виняток, і він практичний: якщо множник більший за одиницю, світлі пікселі впираються в стелю 255 і далі не ростуть. Межа, яка була різкою, стає плоскою, і напрямок у цих місцях таки пливе. Заміряно на сцені цієї теми (128 × 160 пікселів): поки не вигоряє жоден піксель, відхилення напрямку — 0.000°, тобто нуль до останнього знака. При множнику 1.8 у стелю впираються 10 999 пікселів із 20 480 — понад половина кадру, — і напрямок відхиляється в середньому вже на 3.38°. Ті самі числа покаже табло інтерактиву нижче, якщо довести повзунок до 1.80.
Яскравість поїхала, напрямок ні
Повзунок множить усі пікселі на коефіцієнт. Стрілки показують напрямок градієнта в девʼяти точках; праворуч — три числа до й після.
Ось звідки правило, на якому стоїть уся класична робота із зображеннями: напрямок градієнта — найдешевша ознака, стійка до освітлення. Модуль теж корисний, але його треба нормувати; напрямок — ні. Лишається зібрати з напрямків щось компактне.
HOGHOG по кроках
Найвідоміша придумана ознака в історії зору зветься HOG — Histogram of Oriented Gradients, «гістограма орієнтованих градієнтів». Вона зроблена рівно з того, що ми щойно отримали, і будується в чотири кроки.
Розрізаємо вікно сіткою на квадратні комірки (cell) 8 × 8 пікселів. Це те саме компромісне рішення, що в просторовій піраміді: усередині комірки координати втрачаються, між комірками — зберігаються.
У кожній комірці заводимо 9 корзин: 0–20°, 20–40°, …, 160–180°. Чому 180, а не 360? Бо межа, темна зліва й світла справа, і та сама межа навпаки — це одна й та сама межа; напрямок беремо без знака. Кожен із 64 пікселів комірки кидає свій голос у ту корзину, куди потрапив його напрямок.
Голос не одиничний: піксель голосує своїм модулем градієнта. Так різка межа важить багато, а шумовий тремт на рівному фоні — майже нічого. Це рятує ознаку від шуму без жодного порога.
Беремо квадрат 2 × 2 сусідні комірки — це блок (block), разом 4 × 9 = 36 чисел. Ділимо всі 36 на довжину цього вектора з 36 чисел, тобто на корінь із суми їхніх квадратів. Тепер у блоці важать не абсолютні величини, а співвідношення напрямків. Блоки беруть із перекриттям: кожна комірка потрапляє в кілька блоків і щоразу нормується по-різному.
Тепер порахуймо, якої довжини вийде вектор — це чотири ділення, і кожне варто зробити самому, бо саме тут видно, звідки береться славнозвісне число 3780. Класичне вікно детектора пішоходів — 64 × 128 пікселів.
- Комірок. Ділимо на квадрати 8 × 8: по ширині 64 ÷ 8 = 8 комірок, по висоті 128 ÷ 8 = 16. Разом 8 × 16 = 128 комірок.
- Блоків. Блок — квадрат 2 × 2 сусідні комірки, і блоки зсуваються щоразу на одну комірку, тобто перекриваються. Лівий верхній кут блоку може стояти в будь-якій комірці, крім останньої в рядку й останньої в стовпці — інакше блок вилізе за край. Отже (8 − 1) × (16 − 1) = 7 × 15 = 105 блоків.
- Чисел у блоці. 4 комірки × 9 корзин напрямку = 36.
- Разом. 105 × 36 = 3780 чисел.
Порівняй із входом: у вікні 64 × 128 = 8192 пікселі, а на виході 3780 чисел. Стиснення всього вдвічі — HOG не про компактність. Він про те, що ці 3780 чисел не залежать від освітлення й описують форму, а вісім тисяч сирих пікселів не описують нічого, крім самих себе.
HOG по кроках
Тисни «наступний крок» і дивись, як із сирої яскравості виростає вектор ознаки. На третьому кроці кожна комірка малюється «зірочкою»: довжина променя — сила напрямку.
Тепер найцікавіше в темі, і це не теорія, а факт із командного рядка. Класична
демонстрація HOG — детектор пішоходів, який лежить у кожному підручнику двома рядками:
створити cv2.HOGDescriptor, підвантажити готові ваги. В OpenCV 5
класу HOGDescriptor більше немає — не «немає файлу з вагами», а немає
самого класу. Разом із ним викинуто CascadeClassifier, тобто й виявлення
облич каскадами Хаара, другу безсмертну демонстрацію підручників.
cv2 версії
5.0 hasattr(cv2, "HOGDescriptor") дає False. Так само зникли
AKAZE_create і BRISK_create. Лишилися SIFT_create
і ORB_create — на них і побудована решта теми.Бібліотека не викидає класи з примхи — вона викидає те, чим перестали користуватися. І це найкоротша відповідь на питання теми: придуманий детектор пішоходів зник із головної бібліотеки зору, бо його замінили, а SIFT лишився, бо його не замінили. Пояснювати HOG від цього не менш потрібно: це головна придумана ознака в історії, і без неї не зрозуміти, що саме навчилися робити мережі.
КутиКутові точки
HOG описує ціле вікно одним вектором. Але часто потрібне інше: не «що на знімку», а «де саме на другому знімку та сама точка, що й на першому». Панорама, стабілізація, навігація робота — це задачі про точки, а не про картинку цілком. Які точки годяться? Уяви, що дивишся на знімок крізь маленьке віконце й можеш посунути його на кілька пікселів у будь-який бік. Три випадки:
- Рівна пляма — усередині однотонної ділянки. Куди не посунь, у віконці те саме. Знайти цю точку на другому знімку неможливо: таких точок там тисячі.
- Межа — віконце сидить на краю іконки. Посунеш упоперек межі — усе зміниться. Посунеш уздовж — нічого не зміниться. Точку можна знайти з точністю до положення на лінії, але не точніше.
- Кут — там, де сходяться дві межі. Посунь у будь-який бік — вміст віконця зміниться. Ця точка визначена однозначно.
Це і є ідея Гарріса (Harris corner detector): цікава та точка, у якої навіть найменша зміна по всіх напрямках велика. Не найбільша — саме найменша. У межі найбільша зміна теж велика, тому дивитись треба на найгірший напрямок.
Кут проти межі проти плями
Обери місце й покрути напрямок зсуву віконця. Пелюстка праворуч — наскільки змінюється вміст віконця в кожному напрямку; вирішує найкоротший промінь.
Перебирати всі напрямки не треба. Гарріс показав, що зміну вмісту вікна при малому зсуві можна виразити через три суми добутків градієнтів у вікні — gx², gy² і gxgy. Позначмо їх коротко: A — сума квадратів горизонтальних градієнтів у вікні, C — вертикальних, B — сума їхніх добутків. Словами: A каже, наскільки вміст вікна змінюється при зсуві вбік, C — при зсуві вгору-вниз, а B — наскільки ці дві зміни повʼязані між собою. Відгук рахують так:
Що тут за що відповідає. Добуток A·C великий лише тоді, коли вікно змінюється в обох напрямках одразу — у межі один із двох множників малий, і добуток гине. Відніманням B² прибирають випадок, коли обидві зміни насправді одна й та сама, тільки під кутом. Другий доданок — k · (A + C)² — це штраф за «просто багато градієнта»: k тут маленька стала, зазвичай від 0.04 до 0.06, і її єдина робота — заганяти відгук межі в мінус, щоб межу не сплутати з кутом.
Порахуймо всі три випадки на круглих числах. Пляма: градієнтів немає,
A = C = B = 0, значить R = 0. Межа, вертикальна: яскравість міняється тільки
вбік, тож A = 1000, C = 0, B = 0, і R = (1000·0 − 0) − 0.04·1000² = −40 000,
глибокий мінус. Кут: міняється в обидва боки, A = C = 1000, B = 0, і
R = (1000·1000 − 0) − 0.04·2000² = 1 000 000 − 160 000 = +840 000. Три випадки —
три різні знаки й порядки, і тепер кут відбирається одним порівнянням «R більше за
поріг». В OpenCV це
cv2.cornerHarris, а на практиці частіше беруть обгортку
cv2.goodFeaturesToTrack: вона рахує відгук, відкидає слабкі точки й розганяє
решту, щоб вони не тіснились. На нашій сцені вона знаходить 158 точок.
SIFTSIFT: масштаб і поворот
Кут ми знайшли. Але щоб зіставити його з кутом на іншому знімку, потрібен ще дескриптор — короткий вектор, який описує околицю точки. І тут проблема, якої не було в HOG: на другому знімку той самий кут може бути повернутий і вдвічі більший. SIFT (Scale-Invariant Feature Transform) розвʼязує це трьома ідеями, і кожна з них потім повторилась у мережах.
Розмиваємо знімок гаусовим ядром дедалі сильніше й складаємо стосик картинок від різкої до дуже мутної. Це масштабний простір (scale space): нижні шари бачать дрібні деталі, верхні — тільки великі плями. Точку шукають по всьому стосику одразу й запамʼятовують, на якому шарі вона знайшлась — це і є її розмір. Коли на другому знімку предмет удвічі більший, та сама точка знайдеться на шарі вдвічі мутнішому, і дескриптор порахують з околиці вдвічі більшої. Розмір скоротився.
Навколо точки будують гістограму напрямків градієнта (усі 360°, тепер зі знаком) і беруть напрямок найвищого стовпчика. Це «північ» цієї точки. Далі всі координати й усі напрямки в дескрипторі відлічують від цієї півночі. Повернули знімок на 30° — північ повернулась на ті самі 30°, і дескриптор не змінився.
Околицю точки, уже нормовану за розміром і поворотом, ріжуть на 4 × 4 підкомірки й у кожній будують гістограму з 8 напрямків. Разом 128 чисел. Вектор нормують на довжину, обрізають усе більше за 0.2 (щоб один блиск не переважив усе) і нормують ще раз.
Придивись до третьої ідеї: це той самий хід, що в просторовій піраміді й у HOG — сітка клітинок, у кожній гістограма напрямків. Різниця в тому, що сітка тепер не прибита до кадру, а привʼязана до точки: до її розміру й до її півночі.
ORBORB: швидко й бінарно
SIFT точний, але дорогий: 128 чисел типу float32 — це 512 байтів на
точку, а порівняння двох дескрипторів — 128 множень. Для телефона, який зшиває панораму в
реальному часі, це багато. Тому придумали ORB (Oriented FAST and Rotated BRIEF).
Замість гістограм ORB робить 256 порівнянь пар пікселів: узяти два
наперед визначені пікселі околиці й записати один біт — лівий яскравіший за правий чи ні.
256 бітів — це 32 байти типу uint8. Відстань між двома такими дескрипторами
рахують підрахунком розбіжних бітів — це відстань Гемінга (Hamming
distance).
А тепер найважливіше про неї, бо саме тут і криється вся вигода ORB. Порівняти два дескриптори SIFT — це 128 віднімань, 128 множень і 128 додавань дробових чисел. Порівняти два дескриптори ORB — це дві дії над цілими числами, повторені чотири рази. Спершу XOR («виключне або»): побітова операція, яка ставить одиницю там, де біти різні, і нуль там, де однакові — тобто одним рухом розмічає всі розбіжності. Лишається порахувати одиниці, і це вміє окрема інструкція процесора popcount (population count): вона за один такт повертає, скільки одиничних бітів у 64-бітному числі. Дескриптор із 256 бітів — це рівно чотири таких числа, тож уся відстань між двома точками коштує чотири XOR і чотири popcount. Ніяких множень і ніякої дробової арифметики.
Що це дає на масштабі. Зіставити 400 точок першого знімка з 400 точками другого — це 160 000 порівнянь дескрипторів. У SIFT це 160 000 × 128 ≈ 20 мільйонів дробових множень, у ORB — 160 000 × 4 ≈ 640 тисяч цілочислових операцій. Ось чому телефон зшиває панораму на льоту, а не після.
Числа нижче заміряні на синтетичній сцені. Час залежить від машини — важливе тут співвідношення, а не абсолютна величина:
| Дескриптор | точок | байтів на точку | тип | час, мс | усього байтів |
|---|---|---|---|---|---|
| SIFT | 69 | 512 | float32 | 35.9 | 35 328 |
| ORB | 411 | 32 | uint8 | 2.1 | 13 152 |
ORB знайшов у шість разів більше точок, витратив на кожну в шістнадцять разів менше памʼяті й упорався більш ніж на порядок швидше. Платить він точністю кожної окремої пари — але коли точок ушестеро більше, це часто вигідний обмін. Правило вибору просте: телефон або кадр відео — ORB; максимальна надійність зіставлення без тиску часу — SIFT.
Жодна ознака не виграє всюди
Перемкни спотворення й ознаку. Число — косинусна близькість між ознакою оригіналу й ознакою спотвореного кадру; 1.000 означає повний збіг.
ЗіставленняЗіставлення: тут класика виграє
Дійшли до задачі, заради якої SIFT і ORB живі досі: два знімки того самого предмета з різних боків, треба знайти пари «ця точка на першому — та сама, що ця на другому».
Перший підхід очевидний і поганий: для кожної точки першого знімка знайти найближчий дескриптор на другому. Погано тому, що у кожної точки завжди є найближчий сусід, навіть якщо на другому знімку цієї точки немає — вона затулена або вийшла за кадр. Такий пошук повертає повний список пар, і половина з них вигадана.
Рятує тест співвідношення Лоу (Lowe's ratio test), дивовижно простий. Знайди не одного, а двох найближчих сусідів на другому знімку й прийми пару, тільки якщо перший помітно ближчий за другого:
Логіка: якщо точка справді впізнана, її дескриптор близький до одного-єдиного кандидата й далекий від решти. Якщо ж два кандидати однаково близькі, точка невиразна — так буває в повторюваних візерунках і в шумі, — і краще її викинути, ніж загадувати. Класичний поріг Лоу — 0.75.
Тест співвідношення Лоу
Той самий предмет, знятий під іншим кутом і при слабшому світлі. Повзунок — поріг. Зелені лінії — пари, що справді відповідають одна одній; рожеві — хибні.
Після Лоу лишається другий фільтр, геометричний. Якщо на обох знімках один жорсткий
предмет, усі правильні пари мусять узгоджуватись однією геометричною
відповідністю. Алгоритм RANSAC шукає її навмання: бере чотири випадкові пари,
будує по них перетворення, дивиться, скільки решти пар у нього вкладається, і повторює
сотні разів, лишаючи найкращий варіант. Пари, що вклались, звуться внутрішніми (inliers),
решта викидається.
Заміряно на синтетичній сцені, поворот 27° і масштаб 0.75:
| Етап | пар | правильних | частка |
|---|---|---|---|
| усі найближчі сусіди | 69 | 50 | 72 % |
| після тесту Лоу, поріг 0.75 | 51 | 49 | 96 % |
| після RANSAC | 49 | 49 | 100 % |
Сто відсотків. Не «майже» — усі 49 пар, які пройшли RANSAC, виявились правильними, і обидві відкинуті ним пари були хибними. Це той випадок, де придумана ознака не програла й не збирається програвати, і причину варто назвати прямо: у задачі «знайди ту саму точку на двох кадрах» є геометрична відповідь. Ту саму точку визначає будова поверхні, а не статистика класу. Тому SIFT і сьогодні стоїть у зшивачах панорам і у фотограмметрії, а ORB — у SLAM, тобто в системах, де робот чи дрон будує карту приміщення просто по камері й мусить робити це на кожному кадрі. Одна з найвідоміших таких систем так і зветься — ORB-SLAM, і вибір саме ORB у ній не випадковість: там дескриптор рахують тридцять разів на секунду на бортовому процесорі, і кожен зайвий float коштує кадру. Тут придумана ознака не «поки що тримається» — вона найкращий інструмент, і навчена її не витіснила.
МежаДе вона ламається
А тепер друга половина історії. Змінімо задачу з «той самий предмет» на «той самий клас предметів»: другий знімок — теж телефон на столі, але інша модель, світлий чохол, темна тема, інша сітка іконок. Людина скаже «телефон» за чверть секунди. Ознаки міряють інше.
| Порівнюємо | SIFT, пар | HOG, близькість |
|---|---|---|
| той самий телефон, поворот 27° і масштаб 0.75 | 51 | — |
| той самий телефон, поворот 20° | — | 0.396 |
| інша модель телефона, та сама поза | 3 | 0.475 |
Одразу застереження, щоб числа не сплутались: цей замір зроблено на окремій сцені — такій, де є дві різні моделі телефона, а не одна, як в інтерактивах вище. Тому абсолютні рівні тут свої (в інтерактиві 6 HOG на повороті 20° дає 0.237, а не 0.396), і важить не рівень, а порядок: яке з двох порівнянь виявилось ближчим. Прочитай ці числа уважно, бо вони і є вирок.
SIFT: 51 пара проти трьох. Той самий апарат, повернутий і зменшений, дає 51 узгоджену пару; інший апарат того ж класу — три, тобто випадковість. SIFT робить свою роботу бездоганно, просто робота в нього інша: він упізнає екземпляр, а не клас. Для панорами це саме те, що треба; для питання «чи є на фото телефон» — не годиться взагалі.
HOG: 0.475 проти 0.396. Ще гірше. Інша модель телефона схожа на нашу більше (0.475), ніж наш власний телефон, повернутий на двадцять градусів (0.396). Тобто жоден поріг не розділить ці два випадки: постав його між ними — і власний повернутий телефон опиниться в чужих. Придумана ознака помірялась не з тим, з чим ми хотіли.
Це не поодинока невдача, а закономірність, яку видно ще в інтерактиві 6: гістограма переживає поворот (0.998) і гине від світла (0.363), HOG навпаки — світло 1.000, поворот 0.237. Кожна придумана ознака стійка рівно до того, що її автор вирішив викинути — і сліпа рівно до того ж. Автор гістограми викинув координати, автор HOG — абсолютну яскравість; обидва вгадали для своєї задачі й не вгадали для чужої.
Назвімо прямо, чого саме придуманій ознаці бракує, бо «мережі кращі» — це не відповідь. Бракує їй переліку того, чим предмети одного класу дозволено відрізнятись. Автор мусить скласти цей перелік наперед, до першого знімка, і зашити в код: «поворот неважливий, яскравість неважлива, координати неважливі». Для задачі «той самий телефон під іншим кутом» такий перелік складається — там усього кілька пунктів, і всі геометричні. Для задачі «будь-який телефон» він не складається взагалі: туди довелось би вписати інші іконки, інший корпус, інше співвідношення сторін, чохол, тріщину на склі, темну тему, палець на екрані — і це список без кінця, який ще й різний для телефонів, облич і дорожніх знаків. Людина такий список не напише. А от прочитати його з тисячі розмічених знімків, де всі ці телефони вже позначені телефонами, — задача, яку ми вміємо ставити. Ось у чому справжня різниця, і вона не про якість, а про те, звідки береться рішення, що саме викидати.
Звідси й практика тих років. Ознака під пішоходів не годиться під дорожні знаки, під знаки — під обличчя, під обличчя — під літери. Щоразу окрема інженерна робота на місяці: придумати, чим саме цей клас відрізняється від усього іншого, запрограмувати вручну, поміряти, переробити. І кожна така ознака лишалась крихкою до того, чого автор не передбачив: незвичного ракурсу, тіні, затулення, іншого фону.
ALIKED і DISK — детектори ознак, які
не придумані, а навчені на даних. Запустити їх однією командою, як
SIFT_create(), не вийде: ALIKED.create() вимагає обовʼязковим
аргументом шлях до файлу з вагами моделі, а самого файлу в пакеті немає — його
викачують окремо. І це не незручність, а сама суть різниці: придуманій ознаці файл із
числами не потрібен, бо вона вся вміщається в код, який можна прочитати; навченій він
потрібен обовʼязково, бо все, що вона знає, здобуте з даних і лежить у тих числах.
Прочитай цей факт двічі: у тій самій бібліотеці, яка викинула HOG і каскади Хаара, місце
придуманих детекторів зайняли навчені.ПідсумокКінець блоку 1
Блок 1 закінчено. Тема 01 показала, що зображення — це масив чисел. Тема 02 розклала задачі зору за формою відповіді. Тема 03 дала OpenCV як інструмент. Тема 04 зробила згортку руками й довела її до Собеля й Кенні. Ця тема взяла результат згортки й побудувала з нього ознаку — коротке число, за яким знімки можна порівнювати.
Що лишається з тобою назавжди:
- Три вимоги конфліктують. Стійкість, розрізняльність і компактність не бувають максимальними одночасно; будь-яка ознака — точка компромісу.
- Напрямок градієнта — найдешевша стійкість до світла, і на ньому стоїть половина класичного зору.
- Сітка гістограм — той самий прийом, повторений у просторовій піраміді, у HOG і в SIFT: викинути координати всередині клітинки, зберегти між ними.
- Придумана ознака виграє там, де ти точно знаєш, що шукаєш, і програє там, де відповідь статистична, а не геометрична.
І тепер питання, з якого починається наступний блок. Цілу тему ми вручну вирішували, що саме викинути з зображення й що лишити: викинути координати, лишити відтінки; викинути яскравість, лишити напрямки; викинути абсолютні числа в блоці, лишити їхні співвідношення. Кожне таке рішення — здогад інженера, перевірений потім числом.
Але ж у нас є числа наперед: тисячі розмічених знімків, де відомо, що на них зображено. І в курсі машинного навчання ми вже вміємо робити з даних те, що інакше довелося б угадувати руками. Питання звучить так: а чи не можна замість того, щоб вигадувати перетворення й перевіряти його на даних, просто вивчити саме перетворення з даних?
Можна. Ядро згортки з теми 04 — це набір чисел; замість Собеля туди можна поставити параметри й навчити їх градієнтним спуском, як будь-яку іншу вагу. Мережа сама вирішить, що викинути й що лишити, — і зробить це не для «зображень узагалі», а для тієї задачі, яку ти їй дав. Саме звідси починається блок 2, і починається він із питання, чому для зображень не годиться звичайна повнозвʼязна мережа.