Nén nhanh, không mất dữ liệu của một luồng video


14

Tôi có một video đến từ một máy ảnh đứng yên. Cả độ phân giải và FPS đều khá cao. Dữ liệu tôi nhận được ở định dạng Bayer và sử dụng 10 bit cho mỗi pixel. Vì không có loại dữ liệu 10 bit trên nền tảng của tôi, dữ liệu gốc được lưu trữ trong bộ nhớ bằng các từ 16 bit. Tôi muốn thực hiện một số loại nén dữ liệu không mất dữ liệu trước khi truyền dữ liệu qua mạng.

  • Máy ảnh không di chuyển, vì vậy các phần lớn của các khung hình liên tiếp gần như giống hệt nhau - nhưng vẫn không hoàn toàn, do tiếng ồn không thể tránh khỏi (khử nhiễu không phải là một lựa chọn, vì nó được coi là không mất mát và không nên "mất" ngay cả tiếng ồn ).
  • Do FPS cao, ngay cả những phần thay đổi cũng không thay đổi nhiều giữa hai khung hình liên tiếp.
  • Tuy nhiên, có vẻ như máy ảnh cũng rung lên một chút. Rất ít, nhưng vẫn còn, ngay cả các đối tượng đứng yên không hoàn toàn như vậy trong không gian hình ảnh.
  • Việc nén phải được thực hiện nhanh chóng, vì vậy tôi không thể thu thập nhiều khung hình và nén tất cả chúng lại với nhau, nhưng tôi có thể nhìn lại 1 khung hình và sử dụng nó làm tài liệu tham khảo.

Dựa trên những điều trên, suy nghĩ đầu tiên của tôi là đóng gói dữ liệu, để 6 bit dự phòng đó không bị lãng phí trên mỗi từ. Tuy nhiên, tôi nghĩ rằng nếu tôi sử dụng một số mã hóa entropy (ví dụ Huffman, v.v.), thì sự dư thừa đó sẽ được tự động tính đến, do đó không cần đóng gói thêm. Vì vậy, tôi đã làm như sau:

  • Lấy chênh lệch nhị phân giữa hai khung liên tiếp. Phạm vi dữ liệu ban đầu là 0 ~ 1023 (ví dụ 10 bit không dấu). Dữ liệu khác biệt được ký và phạm vi tăng lên -1023 ~ 1023, nhưng biến thể dữ liệu (hoặc thuật ngữ toán học chính xác) trở nên ít hơn nhiều so với dữ liệu gốc, trên thực tế, hầu hết các giá trị gần như không có gì đáng ngạc nhiên .
  • Áp dụng gạo mã hóa cho sự khác biệt. Theo những gì tôi hiểu, có vẻ như là một lựa chọn tốt cho các tập dữ liệu có giá trị số nhỏ.

Điều này giúp tôi giảm khoảng 60% kích thước cho các khung hình 1280x720 và hệ thống thử nghiệm của tôi (Linux trong VirtualBox trên một lõi) có thể thực hiện ~ 40 lần nén như vậy mỗi giây (mà không cần tối ưu hóa nhiều). Không phải là tuyệt vời, nhưng hợp lý, tôi đoán (hoặc là nó?).

Có cách nào tốt hơn không? Bất kỳ sai lầm phổ biến tôi đã làm? Bất kỳ bước chung tôi đã bỏ lỡ? Các khung có độ phân giải cao hơn có thể được sử dụng sau này - tôi có nên mong đợi tốc độ nén tốt hơn cho kích thước khung lớn hơn không?

CẬP NHẬT:

  • Tôi đã sử dụng thư viện này để mã hóa Rice. Thư viện rất chậm (bản thân tác giả mô tả nó như một thứ gì đó để học chứ không phải để sử dụng thực sự), ví dụ, nó đọc và ghi từng bit một trong các vòng lặp, giết chết hiệu suất. Ban đầu nó chỉ cho tôi ~ 20 FPS, sau khi tối ưu hóa rất cơ bản, nó trở thành 40 FPS (như đã báo cáo ở trên), sau đó tôi đã tối ưu hóa nó thêm một chút nữa, nó trở thành 80. Đó là trên một lõi i7 mà không cần vector hóa.
  • Mặc dù vậy, đối với vector hóa, thật không may, tôi không thể nghĩ ra cách để vector hóa mã Rice (thậm chí không biết liệu nó có khả thi hay không - không thể tìm thấy bất kỳ dữ liệu nào về mã Rice, những gì tôi có thể tìm thấy về mã Huffman cho thấy rằng nó tuần tự và không thể được vector hóa một cách hiệu quả, có thể áp dụng cho mã Rice cũng như các mã có độ dài thay đổi khác).
  • Tôi cũng đã thử một cách tiếp cận hoàn toàn khác: chia dữ liệu thành các phần nhỏ (ví dụ như apiece 64 pixel) và sử dụng triệt tiêu không đơn giản. Chúng tôi tìm thấy số lớn nhất trong một khối, ghi số bit cần thiết để thể hiện nó ở đầu khối (trong 4 trường hợp của tôi là bắt buộc), sau đó giảm tất cả các số trong khối xuống cùng một số chút ít. Tôi dự đoán tốc độ nén là xấu, nhưng nếu các mảnh nhỏ, nhiều trong số chúng sẽ không có nhiễu, do đó, chênh lệch nhị phân của chúng có thể giảm xuống còn 4 ~ 6 bit cho mỗi giá trị, và thực tế, chỉ có vậy kém hơn khoảng 5% so với mã Rice, trong khi nhanh gấp khoảng hai lần (ví dụ 160 FPS cho trường hợp của tôi). Tôi đã thử vector hóa nó, nhưng tôi hơi bị vectơ hóa, vì vậy có lẽ vì điều đó tôi chỉ có thể đạt được khoảng x1,8 tốc độ tăng tốc hơn nữa.

Vì các số âm không có số 0 đứng đầu, tôi đã áp dụng mã hóa zigzag sau chênh lệch nhị phân và trước khi triệt tiêu Rice / zero.


Bạn có thể sử dụng một codec tiêu chuẩn như h264 hỗ trợ chế độ 10 bit. "Đặt -crf hoặc -qp thành 0 buộc x264 ở chế độ lossless, cài đặt -preset sau đó chỉ tác động đến tỷ lệ tốc độ / kích thước." (Nhưng tôi không biết liệu nó có quản lý hiệu suất thời gian thực không)
CodeInChaos

@CodesInChaos, nó sẽ làm được gì nhiều cho chỉ hai khung hình?
Headcrab

Có lẽ, thậm chí quan trọng hơn - các codec tiêu chuẩn thậm chí có thể mã hóa hình ảnh của Bayer? Nếu tôi không nhầm, việc chuyển đổi từ Bayer sang RGB liên quan đến phép nội suy, và do đó, không thể đảo ngược.
Headcrab

Câu trả lời:


4

Bạn đã có dự đoán tạm thời, nhưng không có không gian. Để nén tốt hơn với chi phí tốc độ, bạn nên có thể sử dụng các pixel ở trên và bên trái của pixel hiện tại trong khung hiện tại làm dự đoán, cũng như pixel ở cùng vị trí trong khung trước đó. Lý do chỉ nhìn lên và trái giống như lý do chỉ nhìn vào khung trước đó; bạn muốn chỉ dựa vào dữ liệu bạn đã giải mã và giới hạn số lượng dữ liệu bạn phải giữ xung quanh.

Mã gạo có lẽ là một sự đánh đổi tốt giữa hiệu quả và tốc độ, nhưng mã Huffman tĩnh (được bạn tính toán trước trên một mẫu dữ liệu video) có thể hiệu quả hơn và nhanh hơn không kém.

Về tốc độ, hãy đảm bảo rằng mã của bạn đang được vector hóa - bằng cách sử dụng các cờ trình biên dịch và các mẫu mã phù hợp để cho phép trình biên dịch tự động vector hóa, hoặc bằng cách viết tay mã để sử dụng nội tại vectơ hoặc lắp ráp.

Cuối cùng, có khả năng giảm xuống 8 bit cho mỗi pixel không? Rõ ràng điều đó rời khỏi vương quốc "lossless", nhưng nó không chỉ làm giảm kích thước đầu ra được nén của bạn, mà còn với mã vector, có thể tăng thông lượng của bạn lên gấp 2 lần.


Tôi đoán việc giảm 10bpp xuống 8 là không thể, nhưng có thể lưu trữ đồng bằng ở ít bit hơn, theo cách tương tự như UTF-8 sử dụng 1 hoặc đôi khi 2 byte để lưu trữ một ký tự. Nếu đồng bằng gần như bằng 0 mọi lúc, thì sẽ rất hiếm khi thấy tất cả 10 bit thay đổi, và do đó, đáng để nỗ lực xác định 1 hoặc 2 byte để lưu trữ chúng.
gbjbaanb

@gbjbaanb đó là những gì mà mã hóa Rice đạt được. Hầu hết các đồng bằng sẽ nhỏ, và do đó chỉ sử dụng một vài bit.
hobbs

@hobbs, bởi "dự đoán không gian", ý bạn là gì đó giống như thay thế một giá trị pixel x5bằng sự khác biệt (x5 - x4)?
Headcrab

@Headcrab - một cách tiếp cận tôi từng thấy trước đây là sử dụng giá trị trung bình của pixel trước đó và các pixel ở trên và bên trái trong khung hiện tại.
Jules

@Jules nếu một pixel được thay thế bằng một loại giá trị trung bình của các pixel xung quanh, liệu có thể khôi phục giá trị ban đầu của nó không?
Headcrab

0

Bạn có thể được phục vụ tốt nhất bằng cách sử dụng các triển khai nén và giải nén hiện có. Việc triển khai hiện tại của bạn có vẻ tương tự như codec HuffYUV , vì vậy có thể đáng để thử nếu nó hoạt động đủ tốt cho bạn.


libx264 "cài đặt trước cực nhanh" đã phục vụ tôi khá tốt trong lịch sử FWIW ...
rogerdpack

@rogerdpack - Đáng chú ý là cài đặt của libx264 cho kết quả mã hóa không mất dữ liệu trong một đầu ra không tuân thủ H.264 và phá vỡ một số trình phát. Nhưng nó có thể hữu ích cho ứng dụng của OP, ít nhất.
Jules

thú vị bạn có liên kết nào không? Báo cáo lỗi? Cũng lưu ý rằng một video được mã hóa bằng HuffyYUV có lẽ cũng không phải là "thân thiện với người chơi", tôi tưởng tượng :)
rogerdpack
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.