1 pontos por GN⁺ 21 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • Ao modificar a estrutura de múltiplos scans do JPEG progressivo, é possível fazer com que, em vez de a qualidade melhorar conforme o download avança, a imagem já exibida continue sendo trocada por outras imagens
  • Cada scan especifica canal de cor, faixa de frequência DCT e precisão; ao remover alguns marcadores e concatenar JPEGs da mesma resolução, é possível sobrescrever os dados já renderizados
  • Para evitar problemas parecidos com bombas de compressão, os decodificadores limitam a quantidade de scans processados; o Chrome renderiza cerca de 90 frames, enquanto Firefox e outros processam mais
  • Se cada frame usar apenas um scan só de DC, é possível aumentar o número de frames e evitar rastros, mas, por causa das características dos blocos DCT, a resolução resultante cai para 1/16 da original
  • Dá para colocar vários frames em um único JPEG como se fosse vídeo, mas não há informação de temporização, então a velocidade de reprodução depende da latência da rede; em vez de vídeo prático, isso serve melhor para experimentos com HTML e apps de página única usando renderização parcial

Como o JPEG progressivo exibe imagens

  • O JPEG armazena primeiro os componentes de baixa frequência, então, mesmo com apenas parte do arquivo baixada, pode mostrar uma prévia de baixa resolução em vez de uma imagem cortada
  • Os dados comprimidos são divididos em vários scans e cada scan vem com um cabeçalho antes
    • FF DA é o marcador de início de scan
    • Em seguida vêm o campo de tamanho, a quantidade de canais incluídos e seus IDs, e o índice da tabela Huffman
    • Depois são definidos os bins DCT inicial e final e a precisão, e então são armazenados os coeficientes DCT codificados em Huffman
  • O primeiro scan inclui o bin de frequência DC mais baixo dos três canais de cor

YCbCr e melhoria gradual da qualidade

  • Os três canais de cor do JPEG usam YCbCr em vez do RGB comum
    • Y é luminância, então exige qualidade mais alta
    • Cb e Cr são componentes de crominância, então reduzir sua qualidade tem impacto visual menor
    • De forma bem simplificada, Y = G, Cb = B - G, Cr = R - G
  • O JPEG de exemplo preenche os dados gradualmente do scan 0 ao 9
    • Scan 0: armazena Y Cb Cr, bin DCT 0–0, com meia precisão, fornecendo uma prévia de resolução muito baixa
    • Scan 1: adiciona Y, bins 1–5, com 1/4 de precisão, reforçando os detalhes de luminância
    • Scans 2 e 3: adicionam os bins 1–63 de Cb e Cr com meia precisão
    • Scan 4: preenche os bins 6–63 de Y com 1/4 de precisão, cobrindo a faixa deixada pelo scan 1
    • Scan 5: melhora os bins 1–63 de Y para meia precisão
    • Scans 6–9: adicionam o último bit de cada canal, completando a qualidade total
  • Os dados de crominância ficam completos antes da luminância, mas a crominância é armazenada em metade da resolução, ou seja, com 1/4 da quantidade de pixels; por isso, a capacidade total de Cr + Cb ainda é apenas metade da luminância

Trocando a imagem durante o download

  • Como cada scan especifica a faixa de frequência a ser aplicada, é possível criar um JPEG em que scans posteriores sobrescrevem dados de imagem já renderizados
  • Isso é implementado concatenando várias imagens da mesma resolução e filtrando os seguintes marcadores
    • início da imagem (start-of-image)
    • início do frame (start-of-frame)
    • fim da imagem (end-of-image)
  • Dá para fazer isso até com um editor hexadecimal, mas a geração real usa um programa simples em C
  • Se o arquivo for transmitido por uma rede lenta, várias imagens vão sendo trocadas em sequência enquanto o download avança

Limite de scans nos decodificadores

  • A maioria dos decodificadores JPEG para de processar depois de certa quantidade de scans
  • Presume-se que isso exista para evitar problemas parecidos com bombas de compressão, e com a abordagem simples de concatenação fica difícil implementar mais de 9 frames
  • Para criar animações mais longas, é preciso minimizar a quantidade de scans necessária por frame

Por que não dá para usar JPEG Baseline

  • A abordagem de começar com um JPEG Baseline, que usa só um scan, não funciona
  • No modo progressivo, não é possível colocar no mesmo scan o bin 0 de dados DC e os bins 1 ou maiores de dados AC
  • No modo Baseline essa limitação não existe, mas decodificadores Baseline param o processamento depois do primeiro scan
  • Como os dados AC precisam vir depois dos dados DC, o menor frame JPEG progressivo possível consiste em um único scan só de DC

Estrutura e resolução de frames só de DC

  • Como a DCT é processada em blocos de 16×16, mesmo contendo apenas dados DC ainda é possível formar uma imagem em vez de uma cor sólida, mas com resolução de 1/16 da original
  • O frame mínimo usa apenas um scan que armazena os bins DCT 0–0 dos três canais Y Cb Cr com precisão total
  • Nessa configuração, o Chrome renderiza cerca de 90 frames antes de parar o processamento
    • Outros navegadores, como o Firefox, processam mais scans
    • Uma imagem composta por 90 scans funciona em quase todos os navegadores

Troca de frames sem rastros

  • Os rastros da abordagem simples de concatenação surgem porque os scans AC são projetados para refinar dados existentes
  • Em um JPEG progressivo comum, é possível incluir várias etapas de precisão sem aumentar muito o tamanho do arquivo, mas isso não combina com a ideia de substituir a imagem
  • Se forem usados apenas scans só de DC, sem melhoria progressiva real, não ocorre refinamento dos dados AC anteriores, então dá para evitar rastros
  • Frames só de DC são imagens JPEG em conformidade com o padrão, então não é necessário um encoder especial
    • Na especificação do scan, apenas os bins DC dos três canais são indicados, como em 0,1,2:0-0,0,0;

Limites de vídeo em um único JPEG

  • Ao concatenar frames só de DC, é possível colocar um vídeo inteiro dentro de um único arquivo JPEG
  • Não há como adicionar temporização de frames aos scans JPEG, então a velocidade de reprodução depende totalmente da latência da rede
  • Fora usos como Rickrolls incomuns ou pegadinhas, não há aplicações práticas úteis

Experimentos que expandem a renderização parcial

1 comentários

 
Opiniões no Hacker News
  • Já fiz algo muito parecido com PNG entrelaçado Adam7: https://www.da.vidbuchanan.co.uk/adamation/image.png
    A reprodução acaba dependendo da latência da rede, mas configurei o servidor para enviar cada quadro separadamente em intervalos regulares. Como os quadros são pequenos, se a rede não estiver excepcionalmente lenta, é o servidor que determina o momento da reprodução

    • Se você tiver controle do servidor HTTP, também dá para definir Refresh no cabeçalho de resposta para fazer o cliente buscar periodicamente um novo quadro da animação[1]. Uma submissão do IOCCC de 2013 também usa essa técnica para atualizar continuamente um relógio renderizado em PNG[2]
      [1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/...
      [2] https://www.ioccc.org/2013/mills/index.html
      Na linha seguinte, o cabeçalho Refresh está codificado ao contrário: https://github.com/ioccc-src/winner/blob/619f554bbdb19e5003a...
    • Também fiz algo parecido transmitindo vídeo em tempo real com GIF infinito: https://github.com/jbochi/gifstreaming#live-video-streaming-...
    • Enquanto se usam gambiarras para transformar PNG em animação, o APNG parece estar ali do lado gritando: “eu existo!”
    • Fiz a mesma coisa com GIF animado há 20 anos, no trabalho, para adicionar uma camada de qualidade do ar em tempo real a um aplicativo de mapas. As imagens eram renderizadas com Java2D, e também desenvolvi uma versão em JOGL para criar manchas em gradiente em vez de uma nuvem de pontos, mas não consegui concluir porque o administrador do servidor não permitiu instalar drivers OpenGL
  • É uma técnica amaldiçoada, mas certamente combina com isto

    • Grandes empresas de tecnologia devem estar pensando em como usar isso para fingerprinting de navegador
  • Fico me perguntando se essa técnica permitiria esteganografia escondendo dados bem diante dos olhos. A maioria dos programas de análise automática de imagens provavelmente verificaria apenas a última imagem, então parece algo que estudantes poderiam usar para contornar filtros de conteúdo da escola

    • Dá para usar como uma técnica de perturbação de IA: colocar a mensagem para humanos no primeiro quadro e a mensagem para a IA no último. Seria como derrotar a Skynet mandando fotos de gatos uns para os outros
    • Não vejo como isso seria melhor do que esteganografia comum
  • Talvez dê para usar como barra de progresso de tarefas carregadas em paralelo na mesma rede, para que o usuário estime o tempo de espera restante

    • Tenho a lembrança de que lugares como o 4chan bloquearam completamente esse comportamento porque ele era abusado para esconder imagens desagradáveis ou ilegais
  • Se um servidor web gerar um “JPEG” na hora e o enviar ao cliente em fragmentos espaçados no tempo, dá para controlar em certa medida o momento da reprodução. Usando uma webcam como fonte, também é possível ter um “JPEG” que continua indefinidamente

    • Algumas webcams já implementam isso com uma técnica MIME chamada multipart/x-mixed-replace. O servidor instrui o cliente a substituir os dados recém-enviados por novos dados, e funciona só com HTTP comum e JPEG, sem JavaScript
    • Muitas câmeras IP já fazem isso com https://en.wikipedia.org/wiki/Motion_JPEG
    • Também é possível com GIF. Certa vez fiz um CGI de brinquedo que enviava um GIF quadro a quadro e tratava cliques com um mapa de imagem, funcionando como um desktop remoto sem JavaScript. Talvez JPEG tivesse sido melhor, então vale tentar de novo
  • Com um Service Worker, dá para simular uma conexão lenta e controlar a velocidade de reprodução

  • É estranho que o comportamento varie conforme o ambiente. No Firefox desktop, reproduz corretamente, mas no iOS mobile o “vídeo inteiro dentro do JPEG” mostra só 3 quadros quase sólidos — marrom → laranja → vermelho — e um contorno borrado de um gato
    As cores mudam a cada quadro, então dá para saber que está funcionando, mas é difícil chamar isso de vídeo. No desktop, ele roda como um vídeo de verdade, o que me surpreendeu, e parece tocar em algum caso-limite peculiar do decodificador de imagens do iOS

  • Recentemente trabalhei em exibir imagens rapidamente com OpenGL e jpeg-turbo, e ativar o modo progressivo do JPEG deixou a decodificação muito mais lenta. O velho conselho de que JPEG progressivo é bom provavelmente já não é mais válido
    Em décadas, quase nunca vi imagens ficando gradualmente mais nítidas, então também não parece ter muito valor prático

    • Talvez você tenha perdido o momento em que, logo após a página carregar, a imagem aparece um pouco borrada e depois fica nítida na etapa seguinte. Não é como antigamente, quando a primeira etapa era quase irreconhecível, mas ainda há valor em oferecer uma prévia em baixa resolução quase de graça com cerca de 30% do tamanho total do arquivo
    • Uso cjpegli como encoder, e a melhor taxa de compressão vem da combinação padrão de progressivo com 4:4:4 completo, então não é apenas um recurso extra simples
    • Fotos JPEG salvas como progressivas normalmente ficam cerca de 5% menores, e também é possível converter JPEG para o formato progressivo sem perdas. Uma conversão sem perdas para JPEG XL pode economizar ainda mais espaço
    • A decodificação progressiva não serve para tornar a decodificação em si mais rápida, mas para fazer imagens grandes aparecerem mais cedo, especialmente em conexões móveis lentas
      Exemplo: https://youtube.com/watch?v=UphN1_7nP8U
  • Minha primeira ideia foi calcular com dificuldade os coeficientes de alta frequência a partir dos coeficientes “errados” da primeira imagem, mas a solução de simplesmente combinar duas imagens é inteligente. Só concatenar os componentes de baixa frequência de uma imagem com os componentes de alta frequência de outra funciona surpreendentemente bem

  • Fico curioso para saber se seria possível capturar diretamente os componentes de baixa frequência também com uma câmera. Isso também lembra o método de MRI que captura primeiro as baixas frequências no espaço-k