Быстрое сжатие видеопотока без потерь

14

У меня есть видео со стационарной камеры. И разрешение, и FPS довольно высоки. Я получаю данные в формате Байера и использует 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 для моего случая). Я пытался векторизовать его, но я вроде как сосал векторизацию, так что, возможно, из-за этого я мог добиться лишь примерно 1,8 увеличения скорости.

Поскольку отрицательные числа не имеют ведущих нулей, я применил зигзагообразное кодирование после двоичной разницы и до подавления Райса / нуля.

хедкраб
источник
Вы можете использовать стандартный кодек, такой как h264, который поддерживает 10-битный режим. «Установка -crf или -qp в 0 заставляет x264 в режиме без потерь настройки -preset воздействовать только на соотношение скорости и размера». (Но я не знаю, удастся ли это в режиме реального времени)
CodesInChaos
@CodesInChaos, будет ли это много для двух кадров?
Headcrab
Возможно, что еще более важно - стандартные кодеки могут даже кодировать изображения Байера? Если я не ошибаюсь, преобразование Байера в RGB включает интерполяцию и, следовательно, необратим.
Headcrab

Ответы:

4

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

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

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

Наконец, возможно ли снижение до 8 бит на пиксель? Очевидно, что это оставляет сферу «без потерь», но не только уменьшит размер вашего сжатого вывода, но и с векторизованным кодом, возможно, увеличит вашу пропускную способность до 2х.

Hobbs
источник
Я предполагаю, что сокращение 10bpp до 8 невозможно, но можно сохранить дельты в меньшем количестве бит, во многом так же, как UTF-8 использует 1 или иногда 2 байта для хранения символа. Если дельты все время почти равны 0, то очень редко можно увидеть изменение всех 10 битов, поэтому стоит попытаться определить 1 или 2 байта для их хранения.
gbjbaanb
@gbjbaanb это то, что выполняет кодирование Райс. Большинство дельт будет маленьким, и, следовательно, использовать только несколько битов.
Хоббс
@hobbs, под «пространственным предсказанием» вы подразумеваете что-то вроде замены значения пикселя x5на разницу (x5 - x4)?
Headcrab
@Headcrab - подход, который я видел ранее, заключается в использовании медианного значения предыдущего пикселя и пикселей выше и слева в текущем кадре.
Жюль
@Jules Если пиксель заменен каким-то медианным значением окружающих пикселей, возможно ли восстановить его первоначальное значение?
Headcrab
0

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

Жюль
источник
libx264 "preset ultrafast" исторически хорошо мне служил FWIW ...
rogerdpack
@rogerdpack - Стоит отметить, что настройка libx264 для кодирования без потерь приводит к выводу, не совместимому с H.264, и не работает на некоторых проигрывателях. Но это может быть полезно для приложения ОП, по крайней мере.
Жюль
интересно есть ли у вас ссылки на это? Отчет об ошибке? Также обратите внимание, что видео, закодированное с помощью HuffyYUV, вероятно, также не «дружелюбно для игроков», я бы подумал :)
rogerdpack