Mam wideo pochodzące ze stacjonarnej kamery. Zarówno rozdzielczość, jak i liczba klatek na sekundę są dość wysokie. Dane, które otrzymuję, są w formacie Bayera i używają 10 bitów na piksel. Ponieważ na mojej platformie nie ma 10-bitowego typu danych, oryginalne dane są przechowywane w pamięci za pomocą 16-bitowych słów. Chcę zaimplementować pewnego rodzaju bezstratną kompresję danych przed przesłaniem ich przez sieć.
- Aparat się nie porusza, więc duże części kolejnych klatek są prawie identyczne - ale wciąż nie do końca, z powodu nieuniknionego hałasu (odszumianie nie jest opcją, ponieważ powinno być bezstratne i nie powinno „tracić” nawet szumu ).
- Z powodu wysokiej liczby klatek na sekundę nawet zmieniające się części nie zmieniają się znacznie między dwoma kolejnymi klatkami.
- Wygląda jednak na to, że aparat trochę się trzęsie. Bardzo mało, ale nawet nieruchome obiekty nie są całkowicie takie w przestrzeni obrazu.
- Kompresję należy wykonać w locie, więc nie mogę zebrać wielu klatek i skompresować ich wszystkich razem, ale mogę spojrzeć 1 klatkę wstecz i użyć jej jako odniesienia.
W oparciu o powyższe, moją pierwszą myślą było spakowanie bitów danych, aby te 6 nadmiarowych bitów nie było marnowanych na każde słowo. Pomyślałem jednak, że jeśli użyję kodowania entropijnego (np. Huffman itp.), Nadmiarowość zostanie automatycznie uwzględniona, więc nie jest konieczne dodatkowe pakowanie. Więc zrobiłem następujące:
- Wziął binarną różnicę między dwiema kolejnymi ramkami. Pierwotny zakres danych wynosił 0 ~ 1023 (np. 10 bitów bez znaku). Dane różnicowe zostają podpisane, a zakres wzrasta do -1023 ~ 1023, ale zmienność danych (lub prawidłowy termin matematyczny) staje się znacznie mniejsza niż w danych oryginalnych, w rzeczywistości większość wartości, co nie dziwi, jest bliska zeru .
- Zastosowano kodowanie ryżu do różnicy. Z tego, co rozumiem, wygląda to na dobry wybór dla zestawów danych składających się głównie z małych wartości liczbowych.
To daje mi około 60% zmniejszenie rozmiaru ramek 1280x720, a mój system testowy (Linux w VirtualBox na jednym rdzeniu) może wykonać ~ 40 takich kompresji na sekundę (bez dużej optymalizacji). Nie wspaniale, ale rozsądnie, jak sądzę (a może tak?).
Czy są lepsze sposoby? Jakieś typowe błędy, które popełniłem? Jakieś ogólne kroki, które przeoczyłem? Ramki o wyższej rozdzielczości mogą być później używane - czy powinienem oczekiwać lepszych współczynników kompresji dla większych rozmiarów ramek?
UPD .:
- Użyłem tej biblioteki do kodowania Rice'a. Biblioteka jest bardzo wolna (sam autor opisuje ją jako coś do nauki, a nie do prawdziwego użytku), na przykład odczytuje i zapisuje w pętlach fragmenty jeden po drugim, co zabija wydajność. Początkowo dawało mi to tylko ~ 20 FPS, po bardzo podstawowej optymalizacji stało się 40 FPS (jak opisano powyżej), później zoptymalizowałem je trochę więcej, osiągnęło 80. To znaczy na jednym rdzeniu i7 bez wektoryzacji.
- Jeśli chodzi o wektoryzację, niestety nie mogłem wymyślić sposobu na wektoryzację kodu Rice'a (nawet nie wiem czy to w ogóle możliwe - nie mogłem znaleźć żadnych danych na temat kodu Rice'a, co mogłem znaleźć na temat kodu Huffmana sugeruje, że jest sekwencyjny i nie może być skutecznie wektoryzowany, co może dotyczyć zarówno kodu Rice, jak i innych kodów o zmiennej długości).
- Próbowałem też zupełnie innego podejścia: podziel dane na małe części (np. 64-pikselowe) i zastosuj proste tłumienie zera. Znajdujemy największą liczbę w bloku, zapisujemy liczbę bitów wymaganą do jej przedstawienia na początku bloku (w tym przypadku wymagane były 4 dodatkowe bity), a następnie zmniejsz wszystkie liczby w bloku do tej samej liczby bitów Spodziewałem się, że stopień kompresji będzie zły, ale jeśli kawałki są małe, wiele z nich nie będzie miało skoków hałasu, dlatego ich różnicę binarną można zmniejszyć do około 4 ~ 6 bitów na wartość, a tak naprawdę to tylko około 5% gorszy niż kod Rice'a, przy czym jest około dwa razy szybszy (np. 160 FPS w moim przypadku). Próbowałem wektoryzować, ale trochę wkurzam wektoryzację, więc może z tego powodu mogłem osiągnąć tylko około x1,8 dalszego przyspieszenia.
Ponieważ liczby ujemne nie mają zer wiodących, zastosowałem kodowanie zygzakowe po różnicy binarnej i przed tłumieniem Rice / zero.
źródło
Odpowiedzi:
Masz przewidywanie czasowe, ale nie ma przestrzennego. Aby uzyskać lepszą kompresję kosztem szybkości, powinieneś być w stanie wykorzystać piksele powyżej i na lewo od bieżącego piksela w bieżącej klatce jako predyktory, a także piksel w tym samym miejscu w poprzedniej klatce. Powód tylko patrzenia w górę i w lewo jest taki sam jak powód patrzenia tylko na poprzednią klatkę; chcesz polegać tylko na danych, które już zdekodowałeś i ograniczać ich ilość.
Kody ryżowe są prawdopodobnie dobrym kompromisem między wydajnością a szybkością, ale statyczny kod Huffmana (wstępnie obliczony na próbce danych wideo) może być bardziej wydajny i równie szybki.
Jeśli chodzi o szybkość, upewnij się, że twój kod jest wektoryzowany - albo używając odpowiednich flag kompilatora i wzorców kodu, aby umożliwić kompilatorowi automatyczną wektoryzację, lub ręcznie pisząc kod, aby użyć wewnętrznych wektorów lub asemblera.
Wreszcie, czy jest możliwe obniżenie do 8 bitów na piksel? Oczywiście pozostawia to obszar „bezstratny”, ale nie tylko zmniejszyłby rozmiar skompresowanych danych wyjściowych, ale także, z wektoryzowanym kodem, prawdopodobnie zwiększyłby przepustowość do 2x.
źródło
x5
różnicy wartością piksela(x5 - x4)
?Prawdopodobnie najlepiej jest skorzystać z istniejących implementacji kompresji i dekompresji. Twoja istniejąca implementacja wydaje się podobna do kodeka HuffYUV , więc warto spróbować, aby sprawdzić, czy działa ona wystarczająco dobrze dla Ciebie.
źródło