5.9. Арифметичні операції¶
Сімейство функцій малювання з попереднього розділу малює у зображення. Арифметичне сімейство комбінує два зображення в третє – додає значення їхніх пікселів, віднімає одне від одного, обирає мінімум або максимум на кожній позиції. Цей невеликий набір попіксельних арифметичних операцій є основою різниці кадрів, віднімання фону, складання витримки та ряду інших класичних шаблонів.
Арифметичне сімейство класу Image достатньо мале, щоб перерахувати його повністю:
add()– попіксельнеself + other, обрізане до максимуму формату.sub()– попіксельнеself - other, обрізане до0знизу.rsub()– попіксельнеother - self, обрізане до0(та сама арифметика, що йsub, але з переставленими операндами).min()– попіксельний мінімум двох значень.max()– попіксельний максимум.difference()– попіксельне|self - other|, абсолютна різниця.
Плюс дві пов’язані операції над одним зображенням:
invert()– замінює кожен піксель на255 - pixel(або еквівалентний максимум для формату).
Два вихідних градієнти A і B та результат кожної попарної операції, застосованої до них. Кожна операція виконується позиція за позицією – те, що показано в результаті в будь-якому місці, залежить лише від двох вихідних пікселів на тій позиції.¶
5.9.1. Дві форми операнда¶
Кожен із двоіменних методів приймає будь-яку з двох форм для свого другого операнда:
Інший
Imageтих самих розмірів. Арифметика виконується позиція за позицією – результат на(x, y)– це операція, застосована до вихідних пікселів на(x, y)обох зображень.Скалярне значення – ціле число для відтінків сірого, кортеж
(r, g, b)для RGB565. Той самий скаляр застосовується на кожній позиції.
Скалярна форма корисна, коли застосунок хоче зсунути кожен піксель на постійну величину. img.add(40) освітлює все зображення на 40; img.sub((20, 20, 20)) затемнює кожен піксель на 20 на канал; img.max(50) піднімає будь-який піксель нижче 50 до значення 50 і залишає інші незмінними – такий тип операції, що перетворює майже чорний рівень датчика на рівномірний темно-сірий для наступних етапів обробки.
5.9.2. Відсікання¶
Значення пікселів залишаються в межах діапазону формату після кожної операції. Для 8-бітного каналу це означає 0 – 255: будь-що, що перевищило б 255, відсікається до 255, а будь-що, що опустилося б нижче 0, обрізається до 0. Переповнення по колу відсутнє.
Цей вибір має практичне значення. add при освітленні пікселів ніколи не створює раптового артефакту потемніння на яскравому кінці, де математика інакше дала б переповнення; sub при затемненні пікселів ніколи не створює раптового артефакту освітлення на темному кінці, де б інакше стався недотік. Результати залишаються візуально значущими ціною деякої втрати інформації на насичених крайніх значеннях.
Відсікання також пояснює, чому sub і rsub повертають різні результати. img_a.sub(img_b) дає частину a, яка яскравіша за b, і нуль у всіх інших місцях; img_a.rsub(img_b) дає частину b, яка яскравіша за a. Обидва корисні для однобічного виявлення змін – якщо застосунок цікавлять лише пікселі, що стали яскравішими, або лише ті, що стали темнішими – але жоден не вловлює всіх змін між двома кадрами.
5.9.3. Операція різниці¶
Для двобічного виявлення змін слід використовувати difference(), яка обчислює |self - other| на кожній позиції – абсолютну різницю без знаку. Кожен піксель, що змінився в будь-якому напрямку, з’являється як ненульове значення в результаті, а величина пропорційна тому, наскільки він змінився на тій позиції.
Ця властивість – ненульове значення саме там, де два зображення не збігаються – робить difference робочим конем попіксельного виявлення змін. Еталонний кадр, збережений при запуску, і свіжий знімок, оброблені через difference, дають зображення, ненульові пікселі якого позначають кожну позицію, де щось у сцені перемістилося або змінило яскравість.
5.9.4. Обмеження за допомогою маски¶
Усі арифметичні методи приймають аргумент mask, описаний на сторінці «Регіони та маски». Коли передано маску, операція виконується лише на тих позиціях, де маска ненульова; скрізь інде цільове зображення залишається незмінним.
Ця схема використовується у двох шаблонах. Перший – обмеження операції до відомої ділянки: наприклад, додавання двох кадрів лише всередині обмежувального прямокутника виявленого маркера. Другий – побудова складеного кадру по частинах – min по послідовності кадрів всередині маски переднього плану, max по тій самій послідовності всередині доповнюючої маски – такий тип шаблону.
5.9.5. На місці та збереження вхідних даних¶
Арифметичні методи дотримуються операційної конвенції, встановленої раніше: кожен модифікує вихідне зображення на місці та повертає те саме зображення для ланцюгового виклику. Пікселі джерела зникають після виклику – замінюються результатом операції над тим, що було передано як другий операнд.
Коли застосунку потрібно зберегти обидва входи, безпечний шаблон – спочатку скопіювати один із них:
diff = current.copy() # leaves current intact
diff.difference(reference) # diff now holds the absolute difference
Цей шаблон – копіювати, потім оперувати – є основою будь-якого конвеєра різниці кадрів, де еталонний кадр повинен витримати порівняння, щоб його можна було повторно використати на наступному захопленому кадрі.
Маючи шість операцій комбінування, дві операції над одним зображенням, робочого коня абсолютної різниці та ключове слово mask для обмеження, набір інструментів попіксельної арифметики охоплює комбінації яскравості та каналів, необхідні класичному технічному зору. Решта інструментів, подібних до арифметичних, на поверхні працюють побітно, а не з цілими значеннями.