Empêcher la compression de Lepton

17

Dropbox a récemment publié Lepton ( GitHub ), une méthode qui compresse sans perte les images JPEG aller-retour, économisant en moyenne 22%.

En raison du principe du pigeonhole , aucun algorithme de compression général ne peut garantir un fichier plus petit ( général car il ne s'applique pas aux entrées limitées à un format spécifique). Lepton exploite les caractéristiques communes des JPEG, qui, si elles étaient subverties, pourraient le classer pour produire un fichier plus grand que la source.

Exigences

Écrivez un programme qui génère:

  • Une image JPEG / JFIF valide,
  • d'une taille comprise entre 0,5 Mo et 1 Mo,
  • pas plus petit que 256 × 256 px,
  • pas plus grand que 4096 × 4096 px,
  • reconnaissable par Lepton (il peut "compresser" avec succès une .lepimage), et
  • décompresse à un identique .jpg (comme l'entrée).
  • APPx, COMet d'autres métadonnées, les sections de marqueur non graphique sont limitées dans le JPEG (l'injection de quantités arbitraires d'octets aléatoires dans l'image pour approcher asymptotiquement la compression 1: 1 est boiteuse.)
    • un APP0marqueur JFIF est autorisé mais aucune vignette n'est autorisée (devrait être exactement 16 octets)
    • tl; dr Si vous ne placez pas intentionnellement des métadonnées dans un segment EXIF ​​et que vous désactivez toute sorte de miniature que votre bibliothèque de langues de choix souhaite mettre dans l'image, cela devrait être OK.

Postez le code et l'image.

Si vous voulez écrire un programme qui produit une image Lepton qui, une fois convertie, produit un JPEG répondant aux critères, c'est très bien. Elle doit rester identique sur plusieurs cycles JPEG → Lepton → JPEG → ... arbitrairement.

Notation

La taille en octets de l'image Lepton divisée par l'image JPEG source. Une valeur plus élevée (pire compression Lepton) est meilleure. Exécutez Lepton avec des indicateurs et des commutateurs par défaut.


Obtenir Lepton

Un cours intensif de 5 secondes pour construire Lepton:

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

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

Alors ./lepton --helpdevrait vous dire des choses.

Nick T
la source
Je pense que cela pourrait être transformé en un défi de golf de code où vous écrivez du code qui génère une image qui échoue à la compression d'au moins une certaine constante. En fait, il peut suffire de simplement mettre une limite supérieure sur la taille du code qui est beaucoup plus petite que la taille pour coder en dur le jpeg, mais assez grande pour un programme raisonnable.
xnor
3
Y a-t-il une raison de s'attendre à ce que les pixels uniformément aléatoires ne soient pas la meilleure réponse?
feersum
@feersum vous voulez dire comme mon exemple?
Nick T
1
De plus, comme JPEG est un format avec perte, il existe de très nombreuses façons (par exemple, la "qualité", entre autres) de compresser une image donnée. Chaque fichier JPEG comprend quelques tableaux qui dictent la façon dont le reste de l'image est décodé, et ces tableaux peuvent être essentiellement n'importe quoi. Si vous enregistrez une image BMP dans différents programmes, elle sera probablement identique. Si vous enregistrez un JPG dans différents programmes, à moins qu'ils n'utilisent la même bibliothèque principale, probablement pas.
Nick T
2
@feersum Une entrée uniformément aléatoire dans un compresseur JPEG n'entraîne pas une sortie uniformément aléatoire, et c'est sur cette sortie que fonctionne lepton. Si vous pouvez trouver une entrée qui provoque un compresseur JPEG pour produire une sortie aléatoire uniforme, ce serait probablement utile ici et ailleurs.
Sparr

Réponses:

4

Python 3 + mozjpeg + / dev / urandom, 720 × 720: moy. score 102%

Dépend du mozjpegpackage, le code suppose qu'il est installé dans /usr/local/opt/mozjpeg. (sous OS X, c'est simple à installer, il suffit de l'exécuter brew install mozjpeg)

Cela dépend également du /dev/urandomfichier spécial, il est utilisé pour générer des données aléatoires.

Le code mozjpegfournit simplement des données aléatoires au compresseur (au format TGA, car cjpeg le comprend et il a un en-tête très simple), et lui permet de créer un fichier jpeg optimisé. La qualité est définie au maximum car elle rend les coefficients DCT les moins compressibles, et peu importe l'algorithme utilisé pour compresser les données non compressibles.

J'ai vérifié que le cycle jpeg-> lepton-> jpeg est sans perte - c'est vrai.

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

Le code n'est pas joué, évidemment.

Exemple d'image:

Fait amusant: le fichier JPEG généré est plus grand que l'image TGA non compressée source, même si JPEG utilise une compression avec perte.

Fait amusant 2: Imgur (l'hébergement d'images par défaut pour SO) fait un très mauvais travail dans ce fichier - pour une raison quelconque, il le recompresse en qualité inférieure, même s'il est inférieur à 1 Mo. J'ai donc utilisé Github pour télécharger l'image d'exemple.

Fait amusant 3: En général, mozjpeg fait en effet une meilleure compression JPEG tout en restant compatible avec les décodeurs JPEG existants. Et il a également un outil pour optimiser sans perte les fichiers JPEG, aussi - jpegtran.

Afficher un nom
la source
Je pouvais utiliser un RNG multiplateforme (classe SystemRandom par exemple) mais j'étais trop paresseux. C'est trivial et cela devrait donner des résultats similaires.
Afficher le nom
1

Bruit naïf, 1024 × 1024: score 85,55%

Un exemple conforme en Python pour faire bouger les choses. Pas optimisé en aucune façon; lacunes probables:

  • Aucune idée du paramètre de qualité par défaut.
  • Chaque bloc 8x8 a pratiquement la même valeur moyenne exacte (~ 50%) que celle qui lui est adjacente: Lepton dit qu'ils utilisent ces informations pour économiser de l'espace.
  • Tables de quantification et Huffman totalement par défaut (quoi que la bibliothèque décide d'utiliser).

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

bruit

Ensuite, quelques coups pour faire la chose:

./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
Nick T
la source