Compression rapide et sans perte d'un flux vidéo

14

J'ai une vidéo provenant d'une caméra fixe. La résolution et le FPS sont assez élevés. Les données que j'obtiens sont au format Bayer et utilisent 10 bits par pixel. Comme il n'y a pas de type de données 10 bits sur ma plateforme, les données d'origine sont stockées en mémoire à l'aide de mots 16 bits. Je veux implémenter une sorte de compression sans perte des données avant de les transmettre sur un réseau.

  • La caméra ne bouge pas, donc de grandes parties d'images consécutives sont presque identiques - mais toujours pas complètement, en raison du bruit inévitable (le débruitage n'est pas une option, car il est censé être sans perte et ne devrait pas "perdre" même le bruit ).
  • En raison du FPS élevé, même les parties qui changent ne changent pas beaucoup entre deux images consécutives.
  • Cependant, il semble que l'appareil photo tremble également un peu. Très peu, mais quand même, même les objets stationnaires ne le sont pas complètement dans l'espace image.
  • La compression doit être effectuée à la volée, donc je ne peux pas rassembler beaucoup d'images et les compresser toutes ensemble, mais je peux regarder 1 image en arrière et l'utiliser comme référence.

Sur la base de ce qui précède, ma première pensée a été de compresser les données en bits, afin que ces 6 bits redondants ne soient pas gaspillés sur chaque mot. Cependant, je pensais que si j'utilisais du codage entropique (par exemple Huffman, etc.), cette redondance serait automatiquement prise en compte, donc aucun emballage supplémentaire n'est nécessaire. J'ai donc fait ce qui suit:

  • Pris la différence binaire entre deux images consécutives. La plage de données d'origine était comprise entre 0 et 1023 (par exemple, 10 bits non signés). Les données de différence sont signées et la plage augmente à -1023 ~ 1023, mais la variation des données (ou quel est le terme mathématique correct) devient beaucoup moins que dans les données d'origine, en fait, la plupart des valeurs sont, sans surprise, proches de zéro .
  • Codage du riz appliqué à la différence. D'après ce que je comprends, cela semble être un bon choix pour des ensembles de données de valeurs numériques généralement petites.

Cela me donne une réduction d'environ 60% de la taille des images 1280x720, et mon système de test (Linux dans VirtualBox sur un seul cœur) peut effectuer environ 40 compressions de ce type par seconde (sans beaucoup d'optimisation). Pas génial, mais raisonnable, je suppose (ou n'est-ce pas?).

Y a-t-il de meilleures façons? Des erreurs courantes que j'ai faites? Des étapes générales que j'ai manquées? Des images de plus haute résolution peuvent être utilisées plus tard - dois-je m'attendre à de meilleurs taux de compression pour des images plus grandes?

UPD .:

  • J'ai utilisé cette bibliothèque pour l'encodage Rice. La bibliothèque est très lente (l'auteur lui-même la décrit comme quelque chose pour l'apprentissage plutôt que pour une utilisation réelle), par exemple, elle lit et écrit les bits un par un en boucle, ce qui tue les performances. Initialement, cela ne m'a donné que ~ 20 FPS, après une optimisation très basique, il est devenu 40 FPS (comme indiqué ci-dessus), plus tard, je l'ai optimisé un peu plus, il est devenu 80. C'est sur un seul cœur i7 sans vectorisation.
  • En ce qui concerne la vectorisation, cependant, je ne pouvais malheureusement pas penser à un moyen de vectoriser le code Rice (je ne sais même pas si c'est possible - je n'ai pas trouvé de données sur le code Rice, ce que j'ai pu trouver sur le code Huffman suggère que il est séquentiel et ne peut pas être efficacement vectorisé, ce qui peut s'appliquer au code Rice ainsi qu'à d'autres codes de longueur variable).
  • J'ai également essayé une approche complètement différente: diviser les données en petits morceaux (par exemple, comme 64 pixels chacun) et utiliser la suppression du zéro simple. Nous trouvons le plus grand nombre dans un bloc, écrivons le nombre de bits requis pour le représenter au début du bloc (4 bits supplémentaires étaient nécessaires pour cela, dans mon cas), puis réduisons tous les nombres du bloc au même nombre de morceaux. Je m'attendais à ce que le taux de compression soit mauvais, mais si les morceaux sont petits, beaucoup d'entre eux n'auront pas de pics de bruit, donc leur différence binaire peut être réduite à quelque chose comme 4 ~ 6 bits par valeur, et ce n'était, en fait, que environ 5% pire que celui du code Rice, tout en étant environ deux fois plus rapide (par exemple 160 FPS pour mon cas). J'ai essayé de le vectoriser, mais j'aspire un peu à la vectorisation, donc peut-être à cause de cela, je ne pouvais atteindre que x1.8 d'accélération supplémentaire.

Parce que les nombres négatifs n'ont pas de zéros en tête, j'ai appliqué le codage en zigzag après la différence binaire et avant la suppression de Rice / zéro.

Headcrab
la source
Vous pouvez utiliser un codec standard comme h264 qui prend en charge un mode 10 bits. "La définition de -crf ou -qp à 0 force x264 en mode sans perte les paramètres -preset n'affectent alors que le rapport vitesse / taille." (Mais je ne sais pas si cela va gérer les performances en temps réel)
CodesInChaos
@CodesInChaos, cela ferait-il beaucoup pour seulement deux images?
Headcrab
Peut-être, plus important encore - les codecs standard peuvent-ils même encoder des images Bayer? Si je ne me trompe pas, la conversion de Bayer en RVB implique une interpolation et est donc irréversible.
Headcrab

Réponses:

4

Vous avez une prédiction temporelle, mais pas spatiale. Pour une meilleure compression au prix de la vitesse, vous devriez pouvoir utiliser les pixels au-dessus et à gauche du pixel actuel dans l'image actuelle comme prédicteurs, ainsi que le pixel au même emplacement dans l'image précédente. La raison de ne regarder que vers le haut et à gauche est la même que la raison de ne regarder que l'image précédente; vous souhaitez vous fier uniquement aux données que vous avez déjà décodées et limiter la quantité de données que vous devez conserver.

Les codes Rice sont probablement un bon compromis entre efficacité et vitesse, mais un code Huffman statique (précalculé par vous sur un échantillon de données vidéo) pourrait être plus efficace et tout aussi rapide.

En ce qui concerne la vitesse, assurez-vous que votre code est vectorisé - soit en utilisant les bons indicateurs et modèles de code du compilateur pour permettre au compilateur de vectoriser automatiquement, soit en écrivant à la main le code pour utiliser l' intrinsèque ou l'assemblage vectoriel .

Enfin, une baisse à 8 bits par pixel est-elle possible? Évidemment, cela laisse le domaine du "sans perte", mais non seulement cela réduirait la taille de votre sortie compressée, mais cela augmenterait également, avec le code vectorisé, votre débit jusqu'à 2x.

Hobbs
la source
Je suppose que réduire le 10bpp à 8 n'est pas possible, mais il pourrait être possible de stocker les deltas en moins de bits, de la même manière que l'UTF-8 utilise 1 ou parfois 2 octets pour stocker un caractère. Si les deltas sont presque 0 tout le temps, alors il serait assez rare de voir les 10 bits changer, et cela vaut donc la peine de déterminer 1 ou 2 octets pour les stocker.
gbjbaanb
@gbjbaanb c'est ce que le codage Rice accomplit. La plupart des deltas seront petits et n'utiliseront donc que quelques bits.
Hobbs
@hobbs, par "prédiction spatiale", voulez-vous dire quelque chose comme remplacer une valeur de pixel x5par la différence (x5 - x4)?
Headcrab
@Headcrab - une approche que j'ai déjà vue est d'utiliser la valeur médiane du pixel précédent et les pixels au-dessus et à gauche dans l'image actuelle.
Jules
@Jules si un pixel est remplacé par une sorte de valeur médiane des pixels environnants, est-il possible de restaurer sa valeur d'origine?
Headcrab
0

Vous êtes probablement mieux servi en utilisant les implémentations existantes de compression et de décompression. Votre implémentation actuelle semble similaire au codec HuffYUV , il pourrait donc être utile de l'essayer pour voir s'il fonctionne assez bien pour vous.

Jules
la source
libx264 "preset ultra-rapide" m'a bien servi historiquement FWIW ...
rogerdpack
@rogerdpack - Il convient de noter que le réglage de libx264 pour un encodage sans perte se traduit par une sortie qui n'est pas compatible H.264 et se casse sur certains lecteurs. Mais cela pourrait être utile pour l'application du PO, au moins.
Jules
intéressant avez-vous des liens vers cela? Rapport d'erreur? Notez également qu'une vidéo encodée avec HuffyYUV n'est probablement pas "conviviale pour le lecteur uni" non plus, j'imagine :)
rogerdpack