Impedisci la compressione di Lepton


17

Dropbox ha recentemente rilasciato Lepton ( GitHub ), un metodo che comprime senza perdita di immagini JPEG andata e ritorno, con un risparmio medio del 22%.

A causa del principio pigeonhole , non è possibile garantire che nessun algoritmo di compressione generale dia luogo a un file più piccolo ( generale perché non si applica agli input vincolati a un formato specifico). Lepton sfrutta le caratteristiche comuni dei JPEG che, se sovvertiti, potrebbero rovinarlo per produrre un file più grande della fonte.

Requisiti

Scrivi un programma che genera:

  • Un'immagine JPEG / JFIF valida,
  • con una dimensione compresa tra 0,5 MB e 1 MB,
  • non inferiore a 256 × 256 px,
  • non più grande di 4096 × 4096 px,
  • riconoscibile da Lepton (può "comprimere" con successo .lepun'immagine), e
  • si decomprime in un identico .jpg (come input).
  • APPx, COMe altri metadati, le sezioni dei marker non grafici sono limitate nel JPEG (l'iniezione di quantità arbitrarie di byte casuali nell'immagine per avvicinarsi asintoticamente alla compressione 1: 1 è scadente.)
    • è consentito un APP0marker JFIF ma non è consentita alcuna anteprima (dovrebbe essere esattamente di 16 byte)
    • tl; dr Se non stai spingendo intenzionalmente i metadati in un segmento EXIF ​​e disabiliti qualsiasi tipo di anteprima che la tua libreria di lingue di tua scelta vuole mettere nell'immagine, dovrebbe essere OK.

Pubblica il codice e l'immagine.

Se vuoi scrivere un programma che produce un'immagine Lepton che quando convertita produce un JPEG che soddisfa i criteri, va bene. Deve rimanere identico attraverso arbitrariamente molti cicli JPEG → Lepton → JPEG → ....

punteggio

La dimensione in byte dell'immagine Lepton divisa per l'immagine JPEG di origine. Più alto (peggiore compressione di Lepton) è meglio. Esegui Lepton con flag e switch predefiniti.


Ottenere Lepton

Un corso accelerato di 5 secondi per costruire Lepton:

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

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

Quindi ./lepton --helpdovrebbe dirti delle cose.


Penso che questo potrebbe essere riprogrammato in una sfida di golf del codice in cui si scrive codice che genera un'immagine che non riesce a comprimere almeno una costante. In realtà, può essere sufficiente mettere un limite superiore alla dimensione del codice che è molto più piccola della dimensione per codificare il jpeg, ma abbastanza grande per un programma ragionevole.
xnor

3
C'è qualche motivo per aspettarsi che i pixel uniformemente casuali non siano la risposta migliore?
febbraio

@feersum intendi proprio come il mio esempio?
Nick T,

1
Inoltre, poiché JPEG è un formato con perdita, ci sono molti, molti modi (ad esempio "qualità" tra le altre cose) per comprimere una determinata immagine. Ogni file JPEG include un paio di tabelle che dettano come viene decodificato il resto dell'immagine e quelle tabelle possono praticamente essere qualsiasi cosa. Se salvi un'immagine BMP in diversi programmi, probabilmente sarà identica. Se si salva un JPG in programmi diversi, a meno che non utilizzino la stessa libreria di back-end, probabilmente no.
Nick T,

2
@feersum un input uniformemente casuale a un compressore JPEG non produce un output uniformemente casuale, e quell'output è ciò su cui funziona lepton. Se riesci a trovare un input che induce un compressore JPEG a produrre output uniformemente casuali, sarebbe probabilmente utile qui e altrove.
Sparr,

Risposte:


4

Python 3 + mozjpeg + / dev / urandom, 720 × 720: avg. punteggio 102%

Dipende dal mozjpegpacchetto, il codice presuppone che sia installato /usr/local/opt/mozjpeg. (su OS X è banale da installare, basta eseguire brew install mozjpeg)

Inoltre dipende da /dev/urandomun file speciale, viene utilizzato per generare dati casuali.

Il codice fornisce semplicemente dati casuali al mozjpegcompressore (in formato TGA, perché cjpeg lo comprende e ha un'intestazione molto semplice) e gli consente di creare un file jpeg ottimizzato. La qualità è impostata al massimo perché rende i coefficienti DCT i meno comprimibili e non importa molto quale algoritmo viene utilizzato per comprimere i dati non comprimibili.

Ho verificato che il ciclo jpeg-> lepton-> jpeg sia senza perdita - è vero.

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()

Il codice non è giocato a golf, ovviamente.

Immagine di esempio:

Curiosità: il file JPEG generato è più grande dell'immagine TGA non compressa di origine, anche se JPEG utilizza una compressione con perdita.

Curiosità 2: Imgur (l'hosting di immagini predefinito per SO) fa un pessimo lavoro in questo file - per qualche motivo lo ricomprime a qualità inferiore, anche se è inferiore a 1 MB. Quindi ho usato Github per caricare l'immagine di esempio.

Curiosità 3: In generale, mozjpeg offre una migliore compressione JPEG pur rimanendo compatibile con i decodificatori JPEG esistenti. E ha anche uno strumento per ottimizzare senza perdita di file JPEG - jpegtran.


Potrei usare un RNG multipiattaforma (classe SystemRandom per esempio) ma ero troppo pigro. È banale e dovrebbe dare risultati simili.
Visualizza nome

1

Naive Noise, 1024 × 1024: punteggio 85,55%

Un esempio conforme in Python per far rotolare la palla. Non ottimizzato in alcun modo; probabili carenze:

  • Non ho idea di quale sia l'impostazione di qualità predefinita.
  • Ogni blocco 8x8 ha praticamente lo stesso valore medio esatto (~ 50%) a quello adiacente ad esso: Lepton afferma che usano queste informazioni per risparmiare spazio.
  • Quantizzazione totalmente predefinita e tabelle di Huffman (qualunque cosa la libreria decida di usare).

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')

rumore

Quindi alcuni bash per fare la cosa:

./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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.