Compactação rápida e sem perdas de um fluxo de vídeo


14

Eu tenho um vídeo vindo de uma câmera estacionária. Tanto a resolução quanto o FPS são bastante altos. Os dados que recebo estão no formato Bayer e usam 10 bits por pixel. Como não há tipo de dados de 10 bits na minha plataforma, os dados originais são armazenados na memória usando palavras de 16 bits. Desejo implementar algum tipo de compactação sem perda de dados antes de transmiti-lo por uma rede.

  • A câmera não se move, de modo que grandes partes de quadros consecutivos são quase idênticas - mas ainda não completamente, devido ao ruído inevitável (o denoising não é uma opção, pois deve ser sem perdas e não deve "perder" nem mesmo o ruído )
  • Devido ao alto FPS, mesmo as partes que mudam não mudam muito entre dois quadros consecutivos.
  • No entanto, parece que a câmera também treme um pouco. Muito pouco, mas ainda assim, mesmo os objetos estacionários não são completamente assim no espaço da imagem.
  • A compactação deve ser feita em tempo real, para que eu não possa reunir muitos quadros e compactá-los todos juntos, mas posso olhar 1 quadro para trás e usá-lo como referência.

Com base no exposto, meu primeiro pensamento foi empacotar os dados em bits, para que esses 6 bits redundantes não sejam desperdiçados em todas as palavras. No entanto, pensei que, se eu usar alguma codificação de entropia (por exemplo, Huffman etc.), essa redundância será automaticamente levada em consideração, para que não seja necessário empacotamento extra. Então, eu fiz o seguinte:

  • Tomou diferença binária entre dois quadros consecutivos. O intervalo de dados original era de 0 a 1023 (por exemplo, 10 bits não assinados). Os dados de diferença são assinados e o intervalo aumenta para -1023 ~ 1023, mas a variação de dados (ou qual é o termo matemático correto) se torna muito menor do que nos dados originais; na verdade, a maioria dos valores é, sem surpresa, próxima de zero .
  • Arroz aplicado codificação à diferença. Pelo que entendi, parece uma boa opção para conjuntos de dados com valores numéricos principalmente pequenos.

Isso me dá cerca de 60% de redução no tamanho dos quadros de 1280x720, e meu sistema de teste (Linux no VirtualBox em um único núcleo) pode fazer ~ 40 dessas compressões por segundo (sem muita otimização). Não é tão bom, mas razoável, eu acho (ou é?).

Existem maneiras melhores? Algum erro comum que cometi? Alguma etapa geral que eu perdi? Quadros com resolução mais alta podem ser usados ​​posteriormente - devo esperar melhores taxas de compactação para tamanhos de quadro maiores?

UPD .:

  • Eu usei esta biblioteca para codificação Rice. A biblioteca é muito lenta (o próprio autor a descreve como algo para aprender e não para uso real), por exemplo, lê e grava bits um por um em loops, o que prejudica o desempenho. Inicialmente, ele me deu apenas 20 FPS, depois de uma otimização muito básica, tornou-se 40 FPS (conforme relatado acima), depois eu o otimizei mais, tornou-se 80. Isso é em um único núcleo i7 sem vetorização.
  • Quanto à vetorização, infelizmente, não consegui pensar em uma maneira de vetorizar o código Rice (nem sei se é possível - não consegui encontrar nenhum dado sobre o código Rice, o que pude encontrar sobre o código Huffman sugere que é seqüencial e não pode ser vetorizado com eficiência, o que pode se aplicar ao código Rice, bem como a outros códigos de comprimento variável).
  • Eu também tentei uma abordagem completamente diferente: divida os dados em pedaços pequenos (por exemplo, como 64 pixels cada) e use a supressão de zero simples. Encontramos o maior número em um bloco, escrevemos o número de bits necessários para representá-lo no início do bloco (4 bits adicionais foram necessários para isso, no meu caso) e reduzimos todos os números no bloco para o mesmo número de bits. bits. Eu esperava que a taxa de compactação fosse ruim, mas se as peças forem pequenas, muitas delas não terão picos de ruído; portanto, sua diferença binária pode ser reduzida para algo entre 4 e 6 bits por valor, e era, de fato, apenas cerca de 5% pior do que o código de Rice, embora seja duas vezes mais rápido (por exemplo, 160 FPS no meu caso). Eu tentei vetorizá-lo, mas eu meio que sou péssimo em vetorização, então talvez por isso eu tenha conseguido apenas cerca de x 1,8 de maior velocidade.

Como os números negativos não têm zeros à esquerda, apliquei a codificação em zigue-zague após a diferença binária e antes da supressão de Rice / zero.


Você pode usar um codec padrão como o h264, que suporta um modo de 10 bits. "Definir -crf ou -qp como 0 força x264 no modo sem perdas, as configurações -configuram e afetam apenas a relação velocidade / tamanho." (Mas eu não sei se ele vai gerenciar o desempenho em tempo real)
CodesInChaos

@CodesInChaos, faria muito por apenas dois quadros?
Headcrab

Talvez, ainda mais importante - os codecs padrão podem até codificar imagens da Bayer? Se não me engano, a conversão de Bayer para RGB envolve interpolação e, portanto, é irreversível.
Headcrab

Respostas:


4

Você tem previsão temporal, mas não espacial. Para uma melhor compactação ao custo da velocidade, você poderá usar os pixels acima e à esquerda do pixel atual no quadro atual como preditores, bem como o pixel no mesmo local no quadro anterior. O motivo para apenas olhar para cima e para a esquerda é o mesmo motivo para apenas olhar para o quadro anterior; você deseja confiar apenas nos dados já decodificados e limitar a quantidade de dados que precisa manter por perto.

Os códigos de arroz são provavelmente uma boa troca entre eficiência e velocidade, mas um código estático de Huffman (pré-computado por você em uma amostra de dados de vídeo) pode ser mais eficiente e igualmente rápido.

Quanto à velocidade, verifique se o seu código está sendo vetorizado - usando os sinalizadores e padrões de código corretos do compilador para permitir que o compilador se auto-vectorize, ou escrevendo manualmente o código para usar a intrínseca ou montagem do vetor .

Finalmente, está caindo para 8 bits por pixel uma possibilidade? Obviamente, isso está deixando o reino de "sem perdas", mas não apenas reduziria o tamanho da sua saída compactada, como também, com código vetorizado, possivelmente aumentaria sua taxa de transferência em até 2x.


Eu acho que reduzir o 10bpp para 8 não é possível, mas pode ser possível armazenar os deltas em menos bits, da mesma maneira que o UTF-8 usa 1 ou às vezes 2 bytes para armazenar um caractere. Se os deltas são quase 0 o tempo todo, seria muito raro ver todos os 10 bits mudarem e, portanto, vale o esforço de determinar 1 ou 2 bytes para armazená-los.
Gbjbaanb

@gbjbaanb é o que a codificação Rice realiza. A maioria dos deltas será pequena e, portanto, usará apenas alguns bits.
Hbbs

@ Hobbs, por "previsão espacial", você quer dizer algo como substituir um valor de pixel x5pela diferença (x5 - x4)?
Headcrab

@ Headcrab - uma abordagem que eu já usei antes é usar o valor mediano do pixel anterior e os pixels acima e deixados no quadro atual.
Jul16

@ Jules, se um pixel for substituído por algum tipo de valor mediano dos pixels adjacentes, é possível restaurar o valor original?
Julio

0

Você provavelmente é melhor atendido usando implementações existentes de compactação e descompactação. Sua implementação existente parece semelhante ao codec HuffYUV ; portanto, pode valer a pena tentar isso para ver se funciona bem o suficiente para você.


libx264 "preset ultra-rápida" me serviu muito bem historicamente FWIW ...
rogerdpack

@rogerdpack - Vale a pena notar que a configuração da libx264 para codificação sem perdas resulta em uma saída que não é compatível com H.264 e quebras em alguns players. Mas poderia ser útil para a aplicação do OP, pelo menos.
Jules

interessante você tem algum link para isso? Relatório de erro? Observe também que um vídeo codificado com HuffyYUV provavelmente não é "jogador uni amigável" quer, eu imagino :)
rogerdpack
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.