Швидке стискання відеопотоку без втрат


14

У мене є відео із стаціонарної камери. І дозвіл, і 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 подальшого прискорення.

Оскільки від'ємні числа не мають провідних нулів, я застосував зигзагоподібне кодування після двійкової різниці та перед придушенням рису / нуля.


Ви можете використовувати стандартний кодек на зразок h264, який підтримує 10-бітний режим. "Встановлення -crf або -qp до 0 примушує x264 у режимі без втрат - встановити налаштування, а потім вплинути лише на співвідношення швидкості / розміру." (Але я не знаю, чи вдасться це виконати в режимі реального часу)
CodesInChaos

@CodesInChaos, чи багато це зробить лише за два кадри?
Headcrab

Можливо, ще важливіше - чи можуть стандартні кодеки кодувати навіть зображення Bayer? Якщо я не помиляюся, перетворення Bayer в RGB передбачає інтерполяцію, і, отже, незворотно.
Headcrab

Відповіді:


4

У вас є часове передбачення, але просторове. Для кращого стиснення за рахунок швидкості, ви повинні мати можливість використовувати пікселі вгорі та зліва від поточного пікселя в поточному кадрі в якості предикторів, а також піксель у тому самому місці попереднього кадру. Причина лише дивитися вгору та вліво - така ж, як і причина лише дивитися на попередній кадр; ви хочете покладатися лише на дані, які ви вже розшифрували, і обмежити, скільки їх потрібно тримати навколо.

Рисові коди, ймовірно, є хорошим компромісом між ефективністю та швидкістю, але статичний код Хаффмана (попередньо обчислений вами на вибірці відеоданих) може бути більш ефективним і однаково швидким.

Що стосується швидкості, переконайтеся, що ваш код стає векторизованим - або за допомогою правильних прапорів компілятора та шаблонів коду, щоб дозволити компілятору автоматично векторизуватися, або вручну написавши код, щоб використовувати векторні внутрішні елементи або збірку.

Нарешті, чи знижується до 8 біт на піксель можливість? Очевидно, це залишає царину "без втрат", але це не тільки зменшить розмір вашого стисненого виводу, але також, з векторизованим кодом, можливо збільшить пропускну здатність до 2х.


Я думаю, зменшити 10bpp до 8 неможливо, але можна зберегти дельти в меншій кількості біт, приблизно так само, як UTF-8 використовує 1 або іноді 2 байти для зберігання символу. Якщо дельти майже 0 постійно, то досить рідко можна побачити, як всі 10 біт змінюються, і тому варто докласти зусиль, щоб визначити 1 або 2 байти для їх зберігання.
gbjbaanb

@gbjbaanb саме цього виконує кодування Райса. Більшість дельт буде невеликими, і, отже, використовується лише кілька біт.
хобі

@hobbs, під "просторовим прогнозуванням" ви маєте на увазі щось на зразок заміни значення пікселя x5різницею (x5 - x4)?
Headcrab

@Headcrab - підхід, який я бачив раніше, полягає в тому, щоб використовувати середнє значення попереднього пікселя та пікселів вище та зліва в поточному кадрі.
Жуль

@Jules Якщо піксель замінено на якесь середнє значення навколишніх пікселів, чи можна відновити його початкове значення?
Headcrab

0

Вам, мабуть, найкраще користуватися, використовуючи існуючі реалізації стиснення та декомпресії. Ваша існуюча реалізація здається схожою на кодек HuffYUV , тому, можливо, варто спробувати це перевірити, чи працює він для вас досить добре.


libx264 "попередньо встановлений ультрашвидкий" мені досить історично послужив FWIW ...
rogerdpack

@rogerdpack - Варто відзначити, що налаштування libx264 для кодування без втрат призводить до виходу, який не відповідає H.264, і перерви на деяких програвачах. Але це може бути корисним для програми ОП, принаймні.
Жуль

цікаво, чи є у вас посилання на це? Повідомлення про помилку? Також зауважте, що відео, закодоване HuffyYUV, мабуть, теж не є "універсальним гравцем", - я б уявив :)
rogerdpack
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.