Compressão de Thwart Lepton


17

O Dropbox lançou recentemente o Lepton ( GitHub ), um método que comprime sem perdas imagens JPEG de ida e volta, economizando uma média de 22%.

Por causa do princípio pigeonhole , não é possível garantir que qualquer algoritmo de compactação geral resulte em um arquivo menor ( geral porque não se aplica a entradas restritas a um formato específico). O Lepton explora características comuns sobre os JPEGs que, se subvertidos, podem criar um arquivo maior que a fonte.

Exigências

Escreva um programa que gere:

  • Uma imagem JPEG / JFIF válida,
  • com um tamanho entre 0,5 MB e 1 MB,
  • não menor que 256 × 256 px,
  • não maior que 4096 × 4096 px,
  • reconhecível por Lepton (pode "compactar" com êxito uma .lepimagem) e
  • descompacta para um idêntico .jpg (como a entrada).
  • APPx, COM, E outros metadados, secções marcadores não-gráficos são restritas nas JPEG (quantidades arbitrárias injectáveis de bytes aleatórios na imagem para aproximar assimptoticamente 1:. Uma compressão é coxo)
    • um APP0marcador JFIF é permitido, mas nenhuma miniatura é permitida (deve ter exatamente 16 bytes)
    • tl; dr Se você não estiver intencionalmente inserindo metadados em um segmento EXIF ​​e desabilitar qualquer tipo de miniatura que sua biblioteca de idiomas preferida queira colocar na imagem, tudo bem.

Publique o código e a imagem.

Se você deseja escrever um programa que produz uma imagem Lepton que, quando convertida, produz um JPEG que atenda aos critérios, tudo bem. Ele deve permanecer idêntico entre vários ciclos JPEG → Lepton → JPEG → ... arbitrariamente.

Pontuação

O tamanho de byte da imagem Lepton dividido pela imagem JPEG de origem. Maior (pior compressão de Lepton) é melhor. Execute o Lepton com sinalizadores e comutadores padrão.


Obtendo Lepton

Um curso intensivo de 5 segundos para construir o Lepton:

git clone https://github.com/dropbox/lepton.git
cd lepton
./autogen.sh && ./configure && make

# fish shell: ./autogen.sh ;and ./configure ;and make

Então ./lepton --helpdeveria lhe contar as coisas.


Eu acho que isso poderia ser redefinido para um desafio de código de golfe, onde você escreve um código que gera uma imagem que falha na compactação por pelo menos alguma constante. Na verdade, basta apenas colocar um limite superior no tamanho do código que é muito menor que o tamanho para codificar o jpeg, mas grande o suficiente para um programa razoável.
Xnor

3
Existe alguma razão para esperar que pixels uniformemente aleatórios não sejam a melhor resposta?
feersum

@feersum você quer dizer como no meu exemplo?
Nick T

11
Além disso, como o JPEG é um formato com perdas, existem muitas, muitas maneiras (por exemplo, "qualidade" entre outras coisas) para compactar uma determinada imagem. Cada arquivo JPEG inclui algumas tabelas que determinam como o restante da imagem é decodificado, e essas tabelas podem ser basicamente o que for. Se você salvar uma imagem BMP em diferentes programas, provavelmente será idêntica. Se você salvar um JPG em programas diferentes, a menos que eles usem a mesma biblioteca de back-end, provavelmente não.
Nick T

2
A entrada aleatória uniformemente @feersum para um compressor JPEG não resulta em uma saída aleatória uniforme, e é nessa saída que o lepton trabalha. Se você puder criar uma entrada que faça com que um compressor JPEG produza uma saída aleatória uniforme, isso provavelmente será útil aqui e em outros lugares.
Sparr

Respostas:


4

Python 3 + mozjpeg + / dev / urandom, 720 × 720: média pontuação 102%

Depende do mozjpegpacote, o código assume que está instalado /usr/local/opt/mozjpeg. (no OS X é fácil de instalar, basta executar brew install mozjpeg)

Também depende de /dev/urandomarquivo especial, é usado para gerar dados aleatórios.

O código simplesmente alimenta dados aleatórios para o mozjpegcompressor (no formato TGA, porque o cjpeg o entende e possui um cabeçalho muito simples) e permite criar um arquivo jpeg otimizado. A qualidade é definida no máximo porque torna os coeficientes DCT os menos compactáveis ​​e não importa muito qual algoritmo é usado para compactar dados não compactáveis.

Eu verifiquei que o ciclo jpeg-> lepton-> jpeg é sem perdas - é verdade.

import subprocess
from subprocess import PIPE

c_mozjpeg_path = '/usr/local/opt/mozjpeg/bin/cjpeg'
cjpeg_params = '-quality 100 -dc-scan-opt 2 -dct float -targa'
image_size = 720


def write_random_tga_image(w, h, of, rf):
    def wb(value, size):
        of.write(int.to_bytes(value, size, 'little'))

    wb(0, 2)
    wb(3, 1)
    wb(0, 9)
    wb(w, 2)
    wb(h, 2)
    wb(8, 1)
    wb(0, 1)

    data_size = w * h
    while data_size > 0:
        data_size -= of.write(rf.read(data_size))


def main():
    with open('/dev/urandom', 'rb') as rf:
        with open('oops.jpg', 'wb') as f:
            p = subprocess.Popen((c_mozjpeg_path,) + tuple(cjpeg_params.split(' ')), stdin=PIPE, stdout=f)
            write_random_tga_image(image_size, image_size, p.stdin, rf)
            p.communicate()


if __name__ == '__main__':
    main()

Código não é jogado, obviamente.

Imagem de exemplo:

Curiosidade: o arquivo JPEG gerado é maior que a imagem TGA não compactada de origem, mesmo que o JPEG use compactação com perdas.

Curiosidade 2: O Imgur (a hospedagem de imagem padrão para SO) faz um trabalho muito ruim nesse arquivo - por algum motivo, ele o descompacta para uma qualidade inferior, mesmo com menos de 1 MB. Então, usei o Github para fazer upload da imagem de exemplo.

Curiosidade 3: Em geral, o mozjpeg realmente melhora a compactação JPEG enquanto permanece compatível com os decodificadores JPEG existentes. E também possui uma ferramenta para otimizar sem perda de arquivos JPEG - jpegtran.


Eu poderia usar um RNG de plataforma cruzada (classe SystemRandom, por exemplo), mas estava com preguiça. É trivial e deve dar resultados semelhantes.
Exibir nome

1

Ruído ingênuo, 1024 × 1024: pontuação 85.55%

Um exemplo compatível em Python para fazer a bola rolar. Não otimizado de forma alguma; prováveis ​​deficiências:

  • Não faço ideia qual é a configuração de qualidade padrão.
  • Cada bloco 8x8 tem praticamente o mesmo valor médio (~ 50%) do valor adjacente a ele: Lepton diz que eles usam essas informações para economizar espaço.
  • Quantização totalmente padrão e tabelas de Huffman (o que a biblioteca decidir usar).

import numpy as np
from PIL import Image

np.random.seed(0) # make sure it's repeatable.

size = 1024

imgr = np.random.randint(0, 0xFF, (size, size, 3)).astype('uint8')
pimg = Image.fromarray(imgr)
pimg.save('noise.jpg')

ruído

Em seguida, alguns bash para fazer a coisa:

./lepton noise.jpg noise.lep 2>\dev\null # vomits out a lot of technobabble
./lepton noise.lep noise-out.jpg 2>\dev\null

diff -qs noise.jpg noise-out.jpg

SIZE1=$(stat -f "%z" noise.jpg) # http://superuser.com/a/570920/18931
SIZE2=$(stat -f "%z" noise.lep)
RATIO=$(bc <<< "scale=4; $SIZE2/$SIZE1")
echo "$SIZE2/$SIZE1 = $RATIO"

# Files noise.jpg and noise-out.jpg are identical
# 538817/629769 = .8555
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.