JPEG regressivo (Regressive)
(maurycyz.com)- 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 altaCbeCrsã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 DCT0–0, com meia precisão, fornecendo uma prévia de resolução muito baixa - Scan 1: adiciona
Y, bins1–5, com 1/4 de precisão, reforçando os detalhes de luminância - Scans 2 e 3: adicionam os bins
1–63deCbeCrcom meia precisão - Scan 4: preenche os bins
6–63deYcom 1/4 de precisão, cobrindo a faixa deixada pelo scan 1 - Scan 5: melhora os bins
1–63deYpara meia precisão - Scans 6–9: adicionam o último bit de cada canal, completando a qualidade total
- Scan 0: armazena
- 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 + Cbainda é 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–0dos três canaisY Cb Crcom 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;
- Na especificação do scan, apenas os bins DC dos três canais são indicados, como em
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
- Aproveitando a renderização parcial, também é possível fazer experimentos além da troca de frames em JPEG
- Foi implementado o vídeo de Bad Apple em vídeo em HTML puro usando apenas a tag
<dialog> - Também foi implementado um aplicativo interativo de página única sem CSS nem JavaScript, sem precisar embutir os dados diretamente no arquivo
- Para gerar as imagens, foi usado o código C
merge.c
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
Refreshno 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
Refreshestá codificado ao contrário: https://github.com/ioccc-src/winner/blob/619f554bbdb19e5003a...É uma técnica amaldiçoada, mas certamente combina com isto
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
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
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
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 JavaScriptCom 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
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