1 điểm bởi GN⁺ 21 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • Nếu biến đổi cấu trúc nhiều scan của JPEG lũy tiến, thay vì chất lượng ảnh tốt dần khi tải xuống, ảnh đã hiển thị có thể liên tục đổi thành ảnh khác
  • Mỗi scan chỉ định kênh màu, dải tần số DCT và độ chính xác; bằng cách loại bỏ một số marker khỏi các JPEG cùng độ phân giải rồi nối chúng lại, có thể ghi đè dữ liệu render hiện có
  • Decoder giới hạn số scan được xử lý để ngăn các vấn đề tương tự bom nén; Chrome render được tới khoảng 90 khung hình, còn Firefox và một số trình duyệt khác xử lý được nhiều hơn
  • Nếu mỗi khung hình chỉ dùng một scan chứa riêng DC, có thể tăng số khung hình và tránh bóng mờ, nhưng do đặc tính khối DCT, độ phân giải kết quả giảm xuống còn 1/16 so với bản gốc
  • Có thể chứa nhiều khung hình giống video trong một JPEG duy nhất, nhưng vì không có thông tin thời gian nên tốc độ phát phụ thuộc vào độ trễ mạng; phù hợp với các thử nghiệm HTML/ứng dụng một trang tận dụng render từng phần hơn là video thực dụng

Cách JPEG lũy tiến hiển thị hình ảnh

  • JPEG lưu thành phần tần số thấp trước, nên ngay cả khi chỉ tải xuống một phần file, nó có thể hiển thị bản xem trước độ phân giải thấp thay vì ảnh bị cắt
  • Dữ liệu nén được chia thành nhiều scan, mỗi scan có header phía trước
    • FF DA là marker bắt đầu scan
    • Tiếp theo là trường độ dài, số kênh được bao gồm và ID của chúng, cùng chỉ số bảng Huffman
    • Sau khi chỉ định bin DCT bắt đầu/kết thúc và độ chính xác, nó lưu các hệ số DCT được mã hóa Huffman
  • Scan đầu tiên chứa bin tần số DC thấp nhất trong ba kênh màu

YCbCr và cải thiện chất lượng theo từng bước

  • Ba kênh màu của JPEG dùng YCbCr thay vì RGB thông thường
    • Y là độ chói nên cần chất lượng cao
    • Cb, Cr là thành phần sắc sai, nên dù giảm chất lượng cũng ít ảnh hưởng thị giác
    • Nếu biểu diễn rất thô, Y = G, Cb = B - G, Cr = R - G
  • JPEG ví dụ lấp dữ liệu dần dần từ scan 0 đến 9
    • Scan 0: lưu Y Cb Cr, bin DCT 0–0 ở nửa độ chính xác, cung cấp bản xem trước độ phân giải rất thấp
    • Scan 1: thêm Y, bin 1–5 ở 1/4 độ chính xác để bổ sung chi tiết độ chói
    • Scan 2·3: thêm bin 1–63 của Cb, Cr ở nửa độ chính xác
    • Scan 4: điền bin 6–63 của Y ở 1/4 độ chính xác, bù phần còn lại sau scan 1
    • Scan 5: cải thiện bin 1–63 của Y lên nửa độ chính xác
    • Scan 6–9: thêm các bit cuối cùng cho từng kênh để đạt chất lượng đầy đủ
  • Dữ liệu sắc sai hoàn tất trước độ chói, nhưng vì sắc sai được lưu ở nửa độ phân giải, tức 1/4 số pixel, nên tổng dung lượng Cr + Cb cũng chỉ bằng một nửa độ chói

Đổi ảnh trong khi tải xuống

  • Vì mỗi scan ghi rõ dải tần số sẽ áp dụng, có thể tạo JPEG ghi đè dữ liệu ảnh đã render bằng các scan sau
  • Cách triển khai là nối nhiều ảnh cùng độ phân giải lại với nhau, đồng thời lọc bỏ các marker sau
    • bắt đầu ảnh (start-of-image)
    • bắt đầu khung (start-of-frame)
    • kết thúc ảnh (end-of-image)
  • Có thể tạo bằng trình sửa hex, nhưng để sinh thực tế thì dùng một chương trình C đơn giản
  • Khi truyền file qua mạng chậm, nhiều ảnh sẽ lần lượt chuyển đổi trong lúc quá trình tải xuống diễn ra

Giới hạn scan của decoder

  • Hầu hết decoder JPEG sẽ dừng sau khi xử lý một số scan nhất định
  • Đây được cho là giới hạn nhằm tránh các vấn đề tương tự bom nén; với cách nối đơn giản, khó triển khai hơn 9 khung hình
  • Muốn tạo hoạt ảnh dài hơn, cần tối thiểu hóa số scan cần thiết cho mỗi khung hình

Vì sao không thể dùng Baseline JPEG

  • Cách bắt đầu từ Baseline JPEG chỉ dùng một scan không hoạt động
  • Trong chế độ lũy tiến, không thể đặt dữ liệu DC là bin 0 và dữ liệu AC là bin từ 1 trở lên trong cùng một scan
  • Chế độ Baseline không có giới hạn này, nhưng decoder Baseline dừng xử lý sau scan đầu tiên
  • Vì dữ liệu AC phải đến sau dữ liệu DC, khung JPEG lũy tiến nhỏ nhất gồm một scan chỉ chứa DC

Cấu trúc và độ phân giải của khung chỉ DC

  • Vì DCT được xử lý theo đơn vị khối 16×16, ngay cả khi chỉ chứa dữ liệu DC, nó vẫn có thể tạo ra ảnh ở 1/16 độ phân giải của ảnh gốc, chứ không phải ảnh một màu
  • Khung tối thiểu chỉ dùng một scan lưu bin DCT 0–0 của ba kênh Y Cb Cr với độ chính xác đầy đủ
  • Với cấu trúc này, Chrome render khoảng 90 khung hình trước khi dừng xử lý
    • Các trình duyệt khác như Firefox xử lý được nhiều scan hơn
    • Ảnh gồm 90 scan hoạt động trên hầu hết mọi trình duyệt

Chuyển khung không bóng mờ

  • Bóng mờ trong cách nối đơn giản phát sinh vì các scan AC được thiết kế để tinh chỉnh dữ liệu hiện có
  • Trong JPEG lũy tiến thông thường, có thể chứa nhiều mức độ chính xác mà không làm tăng đáng kể kích thước file, nhưng điều đó không phù hợp với cách thay thế ảnh
  • Nếu chỉ dùng các scan DC mà không cải thiện lũy tiến thực sự, sẽ không có việc tinh chỉnh dữ liệu AC trước đó, nhờ đó tránh được bóng mờ
  • Khung chỉ DC là ảnh JPEG tuân thủ chuẩn, nên không cần encoder đặc biệt
    • Trong đặc tả scan, chỉ chỉ định các bin DC của ba kênh, như 0,1,2:0-0,0,0;

Hạn chế của video trong một JPEG duy nhất

  • Nếu nối các khung chỉ DC, có thể chứa toàn bộ video trong một file JPEG
  • Scan JPEG không có cách thêm thời gian cho khung hình, nên tốc độ phát hoàn toàn phụ thuộc vào độ trễ mạng
  • Ngoài Rickroll phi truyền thống hoặc trò đùa, nó không có ứng dụng thực tế

Thử nghiệm mở rộng render từng phần

1 bình luận

 
Ý kiến trên Hacker News
  • Tôi đã làm một thứ rất похожий với PNG xen kẽ Adam7: https://www.da.vidbuchanan.co.uk/adamation/image.png
    Dù việc phát cuối cùng vẫn phụ thuộc vào độ trễ mạng, tôi đã cấu hình để máy chủ gửi riêng từng khung hình theo các khoảng thời gian cố định. Vì các khung hình nhỏ, nếu mạng không chậm bất thường thì máy chủ sẽ quyết định thời điểm phát

    • Nếu có thể kiểm soát máy chủ HTTP, bạn cũng có thể đặt Refresh trong header phản hồi để client định kỳ lấy khung hình hoạt ảnh mới[1]. Một bài dự thi IOCCC năm 2013 cũng liên tục cập nhật chiếc đồng hồ render bằng PNG theo cách này[2]
      [1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/...
      [2] https://www.ioccc.org/2013/mills/index.html
      Dòng tiếp theo có header Refresh được mã hóa ngược: https://github.com/ioccc-src/winner/blob/619f554bbdb19e5003a...
    • Tôi cũng từng làm một thứ tương tự để stream video thời gian thực bằng GIF vô hạn: https://github.com/jbochi/gifstreaming#live-video-streaming-...
    • Trong lúc mọi người dùng mẹo để biến PNG thành hoạt ảnh, APNG đứng bên cạnh như đang hét lên “Tôi cũng tồn tại mà!”
    • Cách đây 20 năm, tôi cũng làm y như vậy bằng GIF động để thêm lớp chất lượng không khí thời gian thực vào ứng dụng bản đồ ở chỗ làm. Ảnh được render bằng Java2D, và tôi còn phát triển một bản JOGL để tạo các khối gradient thay vì đám điểm, nhưng không hoàn thành được vì quản trị viên máy chủ không cho cài driver OpenGL
  • Đúng là một kỹ thuật bị nguyền rủa, nhưng cực kỳ hợp với chỗ này

    • Các tập đoàn công nghệ lớn chắc sẽ nghĩ cách dùng nó cho lấy dấu vân tay trình duyệt
  • Tôi tò mò liệu có thể dùng kỹ thuật này để làm steganography bằng cách giấu dữ liệu ngay trước mắt hay không. Phần lớn chương trình phân tích ảnh tự động có lẽ chỉ kiểm tra ảnh cuối cùng, nên học sinh có thể dùng nó để vượt qua bộ lọc nội dung của trường

    • Nó có thể được dùng như một kỹ thuật gây nhiễu AI, đặt thông điệp cho con người ở khung đầu tiên và thông điệp cho AI ở khung cuối cùng. Kiểu như gửi ảnh mèo cho nhau để đánh bại Skynet
    • Có vẻ không có lý do gì để nó tốt hơn steganography thông thường
  • Tôi nghĩ có thể dùng nó làm thanh tiến trình cho tác vụ tải song song trên cùng một mạng để người dùng ước lượng độ trễ còn lại

    • Tôi nhớ là ở những nơi như 4chan, nó từng bị lạm dụng để che giấu hình ảnh khó chịu hoặc bất hợp pháp, nên họ đã chặn hoàn toàn kiểu hành vi này
  • Nếu máy chủ web tạo “JPEG” tại chỗ rồi gửi cho client thành các mảnh cách nhau theo thời gian, bạn có thể điều khiển thời điểm phát ở mức nào đó. Nếu lấy webcam làm nguồn thì còn có thể tạo “JPEG” kéo dài vô tận

    • Một số webcam đã làm điều này bằng kỹ thuật MIME multipart/x-mixed-replace. Máy chủ bảo client thay thế dữ liệu vừa gửi bằng dữ liệu mới, và nó hoạt động chỉ với HTTP và JPEG thông thường, không cần JavaScript
    • Nhiều camera IP đã triển khai việc này bằng https://en.wikipedia.org/wiki/Motion_JPEG
    • Với GIF cũng làm được. Có lần tôi tạo một CGI đồ chơi gửi GIF theo từng khung hình và xử lý click bằng image map, nên có thể dùng như desktop từ xa mà không cần JavaScript. Có lẽ JPEG sẽ tốt hơn, đáng để thử lại
  • Có thể điều khiển tốc độ phát bằng Service Worker để giả lập kết nối chậm

  • Việc nó hoạt động khác nhau tùy môi trường thật kỳ lạ. Trên Firefox desktop thì phát đúng, nhưng trên iOS mobile, “toàn bộ video bên trong JPEG” gần như chỉ hiện 3 khung màu nâu → cam → đỏ đơn sắc cùng đường nét mờ của con mèo
    Có thể thấy màu thay đổi theo từng khung nên biết là nó đang chạy, nhưng khó mà gọi đó là video. Trên desktop nó phát như video thật nên khá bất ngờ, có vẻ như đã chạm vào một điều kiện biên kỳ quặc của bộ giải mã ảnh trên iOS

  • Gần đây tôi làm việc hiển thị ảnh nhanh bằng OpenGL và jpeg-turbo, và khi bật chế độ progressive của JPEG thì việc giải mã chậm đi đáng kể. Lời khuyên cũ rằng progressive JPEG là tốt có lẽ giờ không còn đúng nữa
    Trong nhiều thập kỷ tôi hầu như không còn thấy ảnh rõ dần theo từng bước, nên cũng không thấy nhiều giá trị thực tế

    • Có thể bạn đã bỏ lỡ khoảnh khắc ảnh hơi mờ ngay sau khi trang tải xong rồi trở nên sắc nét ở bước tiếp theo. Dù không còn đến mức khó nhận ra như trước, nó vẫn có giá trị ở chỗ gần như miễn phí cung cấp bản xem trước độ phân giải thấp chỉ với khoảng 30% tổng dung lượng tệp
    • Tôi dùng cjpegli làm encoder, và vì mặc định progressive kết hợp với 4:4:4 đầy đủ cho tỷ lệ nén tốt nhất nên đây không chỉ là một tính năng phụ
    • Ảnh JPEG lưu ở dạng progressive thường nhỏ hơn khoảng 5%, và cũng có thể chuyển JPEG sang định dạng progressive mà không mất dữ liệu. Nếu chuyển lossless sang JPEG XL thì còn tiết kiệm thêm dung lượng
    • Giải mã progressive không nhằm làm bản thân việc giải mã nhanh hơn, mà để ảnh lớn hiển thị sớm hơn, đặc biệt trên kết nối di động chậm
      Ví dụ: https://youtube.com/watch?v=UphN1_7nP8U
  • Ban đầu tôi nghĩ đến cách tính ngược các hệ số tần số cao từ các hệ số “sai” của ảnh đầu tiên, nhưng cách chỉ đơn giản ghép hai ảnh lại với nhau mới thật thông minh. Chỉ cần nối thành phần tần số thấp của một ảnh với thành phần tần số cao của ảnh kia mà vẫn hoạt động tốt đến đáng ngạc nhiên

  • Tôi tự hỏi liệu camera có thể thu trực tiếp thành phần tần số thấp hay không. Nó cũng khiến tôi nhớ đến MRI, nơi người ta thu tần số thấp trước trong k-space