1P by GN⁺ | ★ favorite | 댓글 1개
  • 점진적 JPEG의 다중 스캔 구조를 변형하면 다운로드가 진행될수록 화질이 좋아지는 대신, 이미 표시된 이미지가 다른 이미지로 계속 바뀌게 할 수 있음
  • 각 스캔은 색상 채널·DCT 주파수 범위·정밀도를 지정하며, 같은 해상도의 JPEG에서 일부 마커를 제거해 이어 붙이면 기존 렌더링 데이터를 덮어쓸 수 있음
  • 디코더는 압축 폭탄과 유사한 문제를 막기 위해 처리할 스캔 수를 제한하며, Chrome은 약 90프레임까지 렌더링하고 Firefox 등은 더 많이 처리함
  • 프레임마다 DC 전용 스캔 하나만 사용하면 프레임 수를 늘리면서 잔상을 피할 수 있지만, DCT 블록 특성상 결과 해상도는 원본의 1/16로 낮아짐
  • 단일 JPEG에 영상처럼 여러 프레임을 담을 수 있으나 타이밍 정보가 없어 재생 속도가 네트워크 지연에 좌우되며, 실용적 영상보다는 부분 렌더링을 활용한 HTML·단일 페이지 앱 실험에 적합함

점진적 JPEG가 이미지를 표시하는 방식

  • JPEG는 저주파 성분을 먼저 저장해 파일을 일부만 다운로드한 상태에서도 잘린 이미지 대신 저해상도 미리보기를 표시할 수 있음
  • 압축 데이터는 여러 스캔(scan) 으로 나뉘며 각 스캔 앞에는 헤더가 붙음
    • FF DA는 스캔 시작 마커임
    • 이어서 길이 필드, 포함된 채널 수와 ID, Huffman 테이블 인덱스가 들어감
    • 시작·종료 DCT 빈과 정밀도를 지정한 뒤 Huffman 부호화된 DCT 계수를 저장함
  • 첫 스캔에는 세 색상 채널에서 가장 낮은 DC 주파수 빈이 포함됨

YCbCr와 단계별 품질 개선

  • JPEG의 세 색상 채널은 일반적인 RGB 대신 YCbCr를 사용함
    • Y는 휘도이므로 높은 품질이 필요함
    • Cb, Cr은 색차 성분이어서 품질을 낮춰도 시각적 영향이 크지 않음
    • 매우 거칠게 나타내면 Y = G, Cb = B - G, Cr = R - G
  • 예제 JPEG는 스캔 0부터 9까지 데이터를 단계적으로 채움
    • 스캔 0: Y Cb Cr, DCT 빈 0–0을 절반 정밀도로 저장해 매우 낮은 해상도의 미리보기를 제공함
    • 스캔 1: Y, 빈 1–5를 1/4 정밀도로 추가해 휘도 세부 정보를 보강함
    • 스캔 2·3: Cb, Cr의 빈 1–63을 절반 정밀도로 추가함
    • 스캔 4: Y의 빈 6–63을 1/4 정밀도로 채워 스캔 1이 남긴 범위를 보완함
    • 스캔 5: Y의 빈 1–63을 절반 정밀도로 개선함
    • 스캔 6–9: 각 채널에 마지막 비트를 추가해 완전한 품질로 만듦
  • 색차 데이터가 휘도보다 먼저 완성되지만, 색차는 절반 해상도, 즉 1/4 픽셀 수로 저장되므로 Cr + Cb 전체 용량도 휘도의 절반에 불과함

다운로드 중 이미지 바꾸기

  • 각 스캔이 적용할 주파수 범위를 명시하므로 이후 스캔으로 이미 렌더링된 이미지 데이터를 덮어쓰는 JPEG를 만들 수 있음
  • 같은 해상도의 이미지 여러 장을 이어 붙이되 다음 마커를 걸러내는 방식으로 구현함
    • 이미지 시작(start-of-image)
    • 프레임 시작(start-of-frame)
    • 이미지 종료(end-of-image)
  • 16진수 편집기로도 만들 수 있지만 실제 생성에는 간단한 C 프로그램을 사용함
  • 파일을 느린 네트워크로 전송하면 다운로드가 진행되는 동안 여러 이미지가 차례로 전환됨

디코더의 스캔 제한

  • 대부분의 JPEG 디코더는 일정 개수의 스캔을 처리하면 중단함
  • 압축 폭탄과 유사한 문제를 피하기 위한 제한으로 추정되며, 단순 결합 방식으로는 9프레임 이상 구현하기 어려움
  • 더 긴 애니메이션을 만들려면 프레임마다 필요한 스캔 수를 최소화해야 함

Baseline JPEG를 사용할 수 없는 이유

  • 스캔 하나만 사용하는 Baseline JPEG에서 시작하는 방식은 작동하지 않음
  • 점진적 모드에서는 한 스캔에 DC 데이터인 빈 0과 AC 데이터인 빈 1 이상을 함께 넣을 수 없음
  • Baseline 모드에는 이 제한이 없지만, Baseline 디코더가 첫 스캔 뒤 처리를 중단함
  • AC 데이터는 DC 데이터 뒤에 와야 하므로 가장 작은 점진적 JPEG 프레임은 DC 전용 스캔 하나로 구성됨

DC 전용 프레임의 구성과 해상도

  • DCT가 16×16 블록 단위로 처리되므로 DC 데이터만 포함해도 단색이 아니라 원본의 1/16 해상도 이미지를 만들 수 있음
  • 최소 프레임은 Y Cb Cr 세 채널의 DCT 빈 0–0을 완전한 정밀도로 저장하는 스캔 하나만 사용함
  • 이 구성에서는 Chrome이 처리를 중단하기 전까지 약 90프레임을 렌더링함
    • Firefox 같은 다른 브라우저는 더 많은 스캔을 처리함
    • 90개 스캔으로 구성된 이미지는 거의 모든 브라우저에서 작동함

잔상 없는 프레임 전환

  • 단순 결합 방식의 잔상은 AC 스캔이 기존 데이터를 정밀화하도록 설계된 데서 발생함
  • 일반적인 점진적 JPEG에서는 파일 크기를 크게 늘리지 않고 여러 정밀도 단계를 담을 수 있지만, 이미지를 교체하는 방식에는 맞지 않음
  • 실제 점진적 개선 없이 DC 전용 스캔만 사용하면 이전 AC 데이터의 정밀화가 일어나지 않아 잔상을 피할 수 있음
  • DC 전용 프레임은 표준을 준수하는 JPEG 이미지여서 특수한 인코더가 필요하지 않음
    • 스캔 명세에는 0,1,2:0-0,0,0;처럼 세 채널의 DC 빈만 지정함

단일 JPEG 영상의 한계

  • DC 전용 프레임을 이어 붙이면 JPEG 파일 하나에 전체 영상을 담을 수 있음
  • JPEG 스캔에는 프레임 타이밍을 추가할 방법이 없어 재생 속도가 전적으로 네트워크 지연에 좌우됨
  • 비전통적인 Rickroll이나 장난 외에는 실용적인 응용 분야가 없음

부분 렌더링을 확장한 실험

댓글과 토론

Hacker News 의견들
  • Adam7 인터레이스 PNG로 매우 비슷한 작업을 해봤음: https://www.da.vidbuchanan.co.uk/adamation/image.png
    재생이 결국 네트워크 지연에 좌우되긴 하지만, 서버가 각 프레임을 일정한 간격으로 따로 보내도록 구성했음. 프레임이 작아서 네트워크가 유난히 느리지 않다면 서버가 재생 시점을 결정함

    • HTTP 서버를 제어할 수 있다면 응답 헤더에 Refresh 를 설정해 클라이언트가 새 애니메이션 프레임을 주기적으로 가져오게 할 수도 있음[1]. 2013년 IOCCC 출품작도 이 기법으로 PNG로 렌더링한 시계를 계속 갱신함[2]
      [1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/...
      [2] https://www.ioccc.org/2013/mills/index.html
      다음 줄에는 Refresh 헤더가 거꾸로 인코딩돼 있음: https://github.com/ioccc-src/winner/blob/619f554bbdb19e5003a...
    • 무한 GIF로 실시간 영상을 스트리밍하는 비슷한 작업도 해봤음: https://github.com/jbochi/gifstreaming#live-video-streaming-...
    • PNG를 애니메이션으로 만들려고 편법을 쓰는 동안, APNG는 옆에서 “나도 존재해!”라고 외치는 듯함
    • 20년 전 직장의 지도 애플리케이션에 실시간 대기질 레이어를 추가하려고 애니메이션 GIF로 같은 작업을 했음. 이미지는 Java2D로 렌더링했고, 점군 대신 그라데이션 덩어리를 만들려고 JOGL 버전도 개발했지만 서버 관리자가 OpenGL 드라이버 설치를 허용하지 않아 완성하지 못함
  • 저주받은 기법이지만 확실히 여기에 어울림

    • 대형 기술 기업들은 이걸 브라우저 지문 채취에 어떻게 활용할지 고민할 듯함
  • 이 기법으로 데이터를 눈앞에 숨기는 스테가노그래피가 가능한지 궁금함. 자동 이미지 분석 프로그램 대부분은 마지막 이미지만 검사할 가능성이 높아, 학생들이 학교의 콘텐츠 필터를 우회하는 데 쓸 수도 있어 보임

    • 사람에게 보낼 메시지는 첫 프레임에, AI에게 보낼 메시지는 마지막 프레임에 넣는 AI 교란 기법으로 쓸 수 있음. 고양이 사진을 서로 보내 Skynet을 물리치는 셈임
    • 일반적인 스테가노그래피보다 나을 방법은 없어 보임
  • 같은 네트워크에서 병렬로 불러오는 작업의 진행 표시줄로 활용해 사용자가 남은 지연 시간을 가늠하게 할 수 있지 않을까 싶음

    • 4chan 같은 곳에서는 불쾌하거나 불법적인 이미지를 숨기는 데 악용돼 이런 동작을 완전히 차단한 것으로 기억함
  • 웹 서버가 즉석에서 “JPEG”를 생성해 시간 간격을 둔 조각으로 클라이언트에 보내면 재생 시점을 어느 정도 제어할 수 있음. 웹캠을 소스로 삼으면 끝없이 이어지는 “JPEG”도 가능함

    • 이미 일부 웹캠이 multipart/x-mixed-replace 라는 MIME 기법으로 이를 구현함. 서버가 방금 보낸 데이터를 새 데이터로 교체하라고 클라이언트에 지시하는 방식이며, JavaScript 없이 일반 HTTP와 JPEG만으로 작동함
    • 많은 IP 카메라가 이미 https://en.wikipedia.org/wiki/Motion_JPEG으로 이를 구현함
    • GIF로도 가능함. 한 번은 GIF를 프레임 단위로 보내고 이미지 맵으로 클릭을 처리해, JavaScript 없이 원격 데스크톱처럼 쓸 수 있는 장난감 CGI를 만들었음. JPEG가 더 나았을 수도 있어 다시 시도해볼 만함
  • Service Worker로 느린 연결을 흉내 내면 재생 속도를 제어할 수 있음

  • 환경에 따라 동작이 다른 점이 이상함. 데스크톱 Firefox에서는 제대로 재생되지만 iOS 모바일에서 “JPEG 안의 전체 영상”은 거의 단색인 갈색→주황색→빨간색 프레임 3개와 흐릿한 고양이 윤곽만 보임
    색이 프레임마다 바뀌어 작동 중인 건 알 수 있지만 영상이라 부르기는 어려움. 데스크톱에서는 실제 영상처럼 재생돼 놀랐고, iOS 이미지 디코더의 특이한 경계 조건을 건드리는 듯함

  • 최근 OpenGL과 jpeg-turbo로 이미지를 빠르게 표시하는 작업을 했는데, JPEG의 프로그레시브 모드를 켜면 디코딩이 크게 느려졌음. 프로그레시브 JPEG가 있으면 좋다는 옛 조언은 이제 유효하지 않을 가능성이 큼
    수십 년간 이미지가 단계적으로 선명해지는 모습을 거의 보지 못했으므로 실질적인 가치도 없어 보임

    • 페이지가 로드된 직후 이미지가 약간 흐렸다가 다음 단계에서 선명해지는 모습을 놓쳤을 수도 있음. 예전처럼 첫 단계가 알아보기 어려울 정도는 아니지만, 전체 파일 크기의 약 30%만으로 저해상도 미리보기를 거의 공짜로 제공하는 가치는 여전히 있음
    • 인코더로 cjpegli를 쓰는데, 기본값인 프로그레시브와 전체 4:4:4 조합에서 압축률이 가장 좋으므로 단순한 부가 기능만은 아님
    • 프로그레시브로 저장한 JPEG 사진은 보통 약 5% 더 작고, JPEG를 프로그레시브 형식으로 무손실 변환하는 것도 가능함. JPEG XL로 무손실 변환하면 공간을 더 절약할 수 있음
    • 프로그레시브 디코딩은 디코딩 자체를 빠르게 하려는 게 아니라, 특히 느린 모바일 연결에서 큰 이미지가 더 빨리 표시되게 하려는 기능임
      예시: https://youtube.com/watch?v=UphN1_7nP8U
  • 첫 이미지의 “잘못된” 계수에서 고주파 계수를 어렵게 계산하는 방법부터 떠올렸는데, 그냥 두 이미지를 합치는 방식이 영리함. 한 이미지의 저주파 성분 뒤에 다른 이미지의 고주파 성분을 이어 붙이는 것만으로 놀랄 만큼 잘 작동함

  • 카메라로도 저주파 성분을 직접 획득할 수 있는지 궁금함. k-공간에서 저주파를 먼저 획득하는 MRI 방식도 떠오름