У мене є відео із стаціонарної камери. І дозвіл, і FPS досить високі. Отримані дані у форматі Bayer і використовують 10 біт на піксель. Оскільки на моїй платформі немає 10-ти бітових даних, вихідні дані зберігаються в пам'яті за допомогою 16-бітних слів. Я хочу здійснити якесь стиснення даних без втрат, перш ніж передавати їх по мережі.
- Камера не рухається, тому великі частини послідовних кадрів майже однакові - але все ж не повністю, через неминучий шум (позначення - це не варіант, оскільки він повинен бути без втрат і не повинен "втрачати" навіть шум ).
- Через високу FPS, навіть частини, що змінюються, не сильно змінюються між двома кадрами поспіль.
- Однак, схоже, камера також трохи трясеться. Дуже мало, але все-таки навіть нерухомі об’єкти не зовсім такі в просторі зображення.
- Стиснення потрібно робити на льоту, тому я не можу зібрати багато кадрів і стиснути їх усі разом, але я можу оглянути 1 кадр назад і використовувати його як орієнтир.
Виходячи з вищесказаного, моя перша думка полягала в тому, щоб розфасувати дані, щоб ці 6 зайвих бітів не витрачалися на кожне слово. Однак я подумав, що якщо я буду використовувати кодування ентропії (наприклад, Хаффман тощо), то надмірність буде автоматично врахована, тому додаткове пакування не потрібно. Тому я зробив наступне:
- Здійснено двійкову різницю між двома послідовними кадрами. Початковий діапазон даних становив 0 - 1023 (наприклад, без підписання 10 біт). Дані про різницю стають підписаними, і діапазон збільшується до -1023 ~ 1023, але зміна даних (або, що є правильним математичним терміном) стає набагато менше, ніж у вихідних даних, насправді більшість значень, не дивно, близькі до нуля .
- До різниці застосовано кодування Райса. Як я розумію, це виглядає як хороший вибір для наборів даних здебільшого малих числових значень.
Це дає мені зменшення розміру приблизно на 60% для кадрів 1280x720, і моя тестова система (Linux у VirtualBox на одному ядрі) може робити ~ 40 таких компресій в секунду (без особливої оптимізації). Я не так чудово, але розумно, я думаю (чи це?).
Чи є кращі способи? Будь-які поширені помилки, які я допустив? Будь-які загальні кроки я пропустив? Кадри з більшою роздільною здатністю можуть бути використані пізніше - чи варто очікувати кращих показників стиснення для більших розмірів кадру?
UPD .:
- Я використав цю бібліотеку для кодування Райса. Бібліотека дуже повільна (сам автор описує це як щось для навчання, а не для реального використання), наприклад, вона читає і записує біти по одному в циклі, що вбиває продуктивність. Спочатку він дав мені приблизно 20 FPS, після дуже базової оптимізації він став 40 FPS (як повідомлялося вище), пізніше я його оптимізував ще трохи, він став 80. Це на одному ядрі i7 без векторизації.
- Щодо векторизації, то, на жаль, я не міг придумати спосіб векторизації коду Райсу (навіть не знаю, чи це взагалі можливо - не міг знайти жодних даних про код Райса, що я міг би знайти про код Хаффмана, що це говорить про те, що це послідовно і не може бути ефективно векторизованим, що може стосуватися коду Райсу, а також інших кодів змінної довжини).
- Я також спробував зовсім інший підхід: розділити дані на невеликі шматочки (наприклад, на 64 пікселя за штуку) і використовувати просте придушення нуля. Ми знаходимо найбільше число в блоці, записуємо кількість бітів, необхідних для його представлення до початку блоку (для цього в моєму випадку потрібно 4 додаткові біти), потім зменшуємо всі числа в блоці до тієї ж кількості біт. Я очікував, що швидкість стиснення буде поганою, але якщо шматки невеликі, багато з них не матимуть шумових сплесків, тому їх бінарну різницю можна зменшити до чогось на зразок 4 ~ 6 біт на значення, і це було, власне, лише приблизно на 5% гірше, ніж у коду Райса, при цьому вдвічі швидше (наприклад, 160 FPS для мого випадку). Я спробував його векторизувати, але я якось висмоктував векторизацію, тому, можливо, через це я міг досягти лише приблизно x1.8 подальшого прискорення.
Оскільки від'ємні числа не мають провідних нулів, я застосував зигзагоподібне кодування після двійкової різниці та перед придушенням рису / нуля.