Komprimieren Sie ein Bild zu einer 4-KB-Vorschau


30

In dieser Herausforderung erstellen Sie einen Bildvorschau-Komprimierungsalgorithmus. Ziel ist es, eine beliebige Bilddatei auf ein Vorschaubild von 4 KB zu reduzieren, mit dem Bilder mit sehr geringer Bandbreite schnell identifiziert werden können.

Sie müssen zwei Programme (oder ein kombiniertes Programm) schreiben: einen Kompressor und einen Dekompressor. Beide müssen eine Datei oder stdin als Eingabe nehmen und in eine Datei oder stdout ausgeben. Der Kompressor muss ein Bild in einem verlustfreien Mainstream-Bildformat (z. B. PNG, BMP, PPM) akzeptieren und eine Datei von höchstens 4096 Bytes ausgeben . Der Dekomprimierer muss alle vom Komprimierer generierten Dateien akzeptieren und ein Bild ausgeben, das der Eingabe möglichst nahe kommt. Beachten Sie, dass es für den Encoder / Decoder keine Größenbeschränkung für den Quellcode gibt, sodass Sie in Ihrem Algorithmus kreativ sein können.

Beschränkungen:

  • Kein Schummeln'. Ihre Programme verwenden möglicherweise keine versteckten Eingaben, speichern Daten im Internet usw. Es ist Ihnen auch untersagt, Funktionen / Daten einzubeziehen, die sich nur auf den Satz von Scoring-Bildern beziehen.

  • Für Bibliotheken / tools / Einbauten Sie sind zu verwenden , erlauben generische Bildverarbeitungsoperationen (Skalierung, Unschärfen, Farbraumtransformation, etc.), aber nicht Bilddekodierungs / Codierung / Kompressionsoperationen (außer Kompressoreingang und Dekompressor - Ausgabe). Generische Komprimierung / Dekomprimierung ist ebenfalls nicht zulässig . Es ist beabsichtigt, dass Sie Ihre eigene Komprimierung für diese Herausforderung implementieren.

  • Die Abmessungen des vom Dekomprimierer ausgegebenen Bildes müssen genau mit denen der Originaldatei übereinstimmen, die dem Komprimierer übergeben wurde. Sie können davon ausgehen, dass die Bildabmessungen in beiden Richtungen 2 16 nicht überschreiten .

  • Ihr Kompressor muss auf einem durchschnittlichen Consumer-PC in weniger als 5 Minuten ausgeführt werden, und der Dekomprimierer muss in weniger als 10 Sekunden ausgeführt werden, damit ein Bild im folgenden Satz angezeigt wird.

Wertung

Fügen Sie nach der Komprimierung anhand Ihrer Antwort ein verlustfreies Bildalbum des Testkorpus bei, um eine schnelle Überprüfung und einen visuellen Vergleich zu ermöglichen .

Ihr Kompressor wird mit den folgenden Bildern getestet :

sternenklar Quelle Zimmer Regenbogen
Spanne Lama Kind Julia

Sie können alle Bilder in einer Zip-Datei hier herunterladen .

Ihr Ergebnis ist der durchschnittliche strukturelle Ähnlichkeitsindex für Ihren Kompressor auf allen Bildern. Wir werden Open Source dssimfür diese Herausforderung verwenden. Es lässt sich leicht aus dem Quellcode erstellen oder wenn Sie auf Ubuntu arbeiten, hat es auch eine PPA. Es wird bevorzugt, wenn Sie Ihre eigene Antwort erhalten, aber wenn Sie nicht wissen, wie C-Anwendungen erstellt werden, und wenn Sie Debian / Ubuntu nicht ausführen, können Sie sich von jemand anderem bewerten lassen. dssimErwartet Eingabe / Ausgabe in PNG, konvertieren Sie Ihre Ausgabe also zuerst in PNG, wenn Sie in einem anderen Format ausgeben.

Um das Scoring schmerzlos zu machen, folgt ein schnelles Hilfsskript für Python python score.py corpus_dir compressed_dir:

import glob, sys, os, subprocess

scores = []
for img in sorted(os.listdir(sys.argv[1])):
    ref, preview = (os.path.join(sys.argv[i], img) for i in (1, 2))
    sys.stdout.write("Comparing {} to {}... ".format(ref, preview))
    out = subprocess.check_output(["dssim", ref, preview]).decode("utf-8").split()[0]
    print(out)
    scores.append(float(out))

print("Average score: {:.6f}".format(sum(scores) / len(scores)))

Die niedrigste Punktzahl gewinnt.


Muss das komprimierte Bild sichtbar sein?
Eumel

2
@Eumel Du kannst den Dekomprimierer als Betrachter betrachten. Nein, Ihr komprimiertes Format kann beliebig sein und liegt ganz bei Ihnen. Erst nach der Dekomprimierung muss ein sichtbares Bild ausgegeben werden.
Orlp

7
You may assume that the image dimensions do not exceed 2^32 in either direction.Ist das nicht ein bisschen übertrieben? Das heißt, ich muss bis zu 16 Bytes verwenden, um ein Paar (x, y) Koordinaten zu speichern. Nur wenige Bilddateien haben in beiden Richtungen Abmessungen von mehr als 2 ^ 16 (65536) Pixeln, und 2 ^ 11 ist für alle Bilder im Korpus ausreichend.
Peter Olson

@ PeterOlson Ich werde es ändern 2^16.
Orlp

@orlp Die Regeln legen fest, dass der Dekomprimierer weniger als zehn Sekunden benötigen muss, um ein Bild zu dekodieren. Die Idee, die ich habe, wird möglicherweise einige Minuten dauern, um Referenzdateien zu generieren, die in nachfolgenden Aufrufen des Dekomprimierers verwendet werden. Das heißt, es handelt sich um eine einmalige Aktivität, die dem "Installieren" einer Anwendung ähnelt. Wäre diese Lösung disqualifiziert?
Moogie

Antworten:


8

Python mit PIL, Punktzahl 0.094218

Kompressor:

#!/usr/bin/env python
from __future__ import division
import sys, traceback, os
from PIL import Image
from fractions import Fraction
import time, io

def image_bytes(img, scale):
    w,h = [int(dim*scale) for dim in img.size]
    bio = io.BytesIO()
    img.resize((w,h), Image.LANCZOS).save(bio, format='PPM')
    return len(bio.getvalue())

def compress(img):
    w,h = img.size
    w1,w2 = w // 256, w % 256
    h1,h2 = h // 256, h % 256
    n = w*h
    total_size = 4*1024 - 8 #4 KiB minus 8 bytes for
                            # original and new sizes
    #beginning guess for the optimal scaling
    scale = Fraction(total_size, image_bytes(img, 1))
    #now we do a binary search for the optimal dimensions,
    # with the restriction that we maintain the scale factor
    low,high = Fraction(0),Fraction(1)
    best = None
    start_time = time.time()
    iter_count = 0
    while iter_count < 100: #scientifically chosen, not arbitrary at all
        #make sure we don't take longer than 5 minutes for the whole program
        #10 seconds is more than reasonable for the loading/saving
        if time.time() - start_time >= 5*60-10:
            break
        size = image_bytes(img, scale)
        if size > total_size:
            high = scale
        elif size < total_size:
            low = scale
            if best is None or total_size-size < best[1]:
                best = (scale, total_size-size)
        else:
            break
        scale = (low+high)/2
        iter_count += 1
    w_new, h_new = [int(dim*best[0]) for dim in (w,h)]
    wn1,wn2 = w_new // 256, w_new % 256
    hn1, hn2 = h_new // 256, h_new % 256
    i_new = img.resize((w_new, h_new), Image.LANCZOS)
    bio = io.BytesIO()
    i_new.save(bio, format='PPM')
    return ''.join(map(chr, (w1,w2,h1,h2,wn1,wn2,hn1,hn2))) + bio.getvalue()

if __name__ == '__main__':
    for f in sorted(os.listdir(sys.argv[1])):
        try:
            print("Compressing {}".format(f))
            with Image.open(os.path.join(sys.argv[1],f)) as img:
                with open(os.path.join(sys.argv[2], f), 'wb') as out:
                    out.write(compress(img.convert(mode='RGB')))
        except:
            print("Exception with {}".format(f))
            traceback.print_exc()
            continue

Dekomprimierer:

#!/usr/bin/env python
from __future__ import division
import sys, traceback, os
from PIL import Image
from fractions import Fraction
import io

def process_rect(rect):
    return rect

def decompress(compressed):
    w1,w2,h1,h2,wn1,wn2,hn1,hn2 = map(ord, compressed[:8])
    w,h = (w1*256+w2, h1*256+h2)
    wc, hc = (wn1*256+wn2, hn1*256+hn2)
    img_bytes = compressed[8:]
    bio = io.BytesIO(img_bytes)
    img = Image.open(bio)
    return img.resize((w,h), Image.LANCZOS)


if __name__ == '__main__':
    for f in sorted(os.listdir(sys.argv[1])):
        try:
            print("Decompressing {}".format(f))
            with open(os.path.join(sys.argv[1],f), 'rb') as img:
                decompress(img.read()).save(os.path.join(sys.argv[2],f))
        except:
            print("Exception with {}".format(f))
            traceback.print_exc()
            continue

Beide Skripte nehmen Eingaben über Befehlszeilenargumente als zwei Verzeichnisse (Eingabe und Ausgabe) entgegen und konvertieren alle Bilder im Eingabeverzeichnis.

Die Idee ist, eine Größe zu finden, die unter 4 KiB passt und dasselbe Seitenverhältnis wie das Original aufweist, und einen Lanczos-Filter zu verwenden, um eine möglichst hohe Qualität des heruntergerechneten Bildes zu erzielen.

Imgur Album von komprimierten Bildern, nach der Größenänderung auf die ursprünglichen Dimensionen

Scoring-Skriptausgabe:

Comparing corpus/1 - starry.png to test/1 - starry.png... 0.159444
Comparing corpus/2 - source.png to test/2 - source.png... 0.103666
Comparing corpus/3 - room.png to test/3 - room.png... 0.065547
Comparing corpus/4 - rainbow.png to test/4 - rainbow.png... 0.001020
Comparing corpus/5 - margin.png to test/5 - margin.png... 0.282746
Comparing corpus/6 - llama.png to test/6 - llama.png... 0.057997
Comparing corpus/7 - kid.png to test/7 - kid.png... 0.061476
Comparing corpus/8 - julia.png to test/8 - julia.png... 0.021848
Average score: 0.094218

Ich habe gerade festgestellt, dass Ihre Lösung WebP verwendet, was nicht zulässig ist. Ihre Lösung ist ungültig.
Orlp

@orlp Die Verwendung von PPM, einem unkomprimierten Format, wurde behoben.
Mego

In Ordung. Diese Herausforderung zeigt jedoch eine gewisse Schwäche von DSSIM. Ich würde argumentieren, dass die meisten Bilder von Moogie wesentlich besser aussehen.
Orlp

@orlp Sie sehen gut aus wie Thumbnails. Wenn sie (mit Lanczos) auf ihre ursprünglichen Abmessungen vergrößert werden, sehen sie in etwa gleich oder schlechter aus. Ich arbeite daran, Thumbnails meiner Ausgabe hochzuladen.
Mego

7

Java (Vanille, sollte mit Java 1.5+ funktionieren), Score 0.672

Es erzeugt keine besonders guten Dssim-Scores, aber meiner Meinung nach erzeugt es menschlichere Bilder ...

sternenklar Quelle Zimmer Regenbogen
Spanne Lama Kind Julia

Album: http://imgur.com/a/yL31U

Scoring-Skriptausgabe:

Comparing corpus/1 - starry.png to test/1 - starry.png... 2.3521
Comparing corpus/2 - source.png to test/2 - source.png... 1.738
Comparing corpus/3 - room.png to test/3 - room.png... 0.1829
Comparing corpus/4 - rainbow.png to test/4 - rainbow.png... 0.0633
Comparing corpus/5 - margin.png to test/5 - margin.png... 0.4224
Comparing corpus/6 - llama.png to test/6 - llama.png... 0.204
Comparing corpus/7 - kid.png to test/7 - kid.png... 0.36335
Comparing corpus/8 - julia.png to test/8 - julia.png... 0.05
Average score: 0.672

Die Kompressionskette:

1. if filter data has already been generated goto step 6
2. create sample images from random shapes and colours
3. take sample patches from these sample images
4. perform k-clustering of patches based on similarity of luminosity and chomanosity.
5. generate similarity ordered lists for each patch as compared to the other patches.
6. read in image
7. reduce image size to current multiplier * blocksize
8. iterate over image comparing each blocksize block against the list of clustered luminosity patches and chromanosity patches, selecting the closest match
9. output the index of the closet match from the similarity ordered list (of the previous block) (repeat 8 for chromanosity)
10. perform entropy encoding using deflate.
11. if output size is < 4096 bytes then increment current multiplier and repeat step 7
12. write previous output to disk.

Zum Dekomprimieren füllen Sie die Blockindizes auf, lesen Sie sie und geben Sie den entsprechenden Patch in die Ausgabedatei aus. Ändern Sie dann die Größe auf die ursprünglichen Abmessungen.

Leider ist der Code zu groß für Stackoverflow und kann unter https://gist.github.com/anonymous/989ab8a1bb6ec14f6ea9 gefunden werden

Zu rennen:

Usage: 
       For single image compression: java CompressAnImageToA4kibPreview -c <INPUT IMAGE> [<COMPRESSED IMAGE>]
       For multiple image compression: java CompressAnImageToA4kibPreview -c <INPUT IMAGES DIR> [<COMPRESSED IMAGE DIR>]
       For single image decompression: java CompressAnImageToA4kibPreview -d <COMPRESSED IMAGE> [<DECOMPRESSED IMAGE>
       For multiple image decompression: java CompressAnImageToA4kibPreview -d <COMPRESSED IMAGE DIR> [<DECOMPRESSED IMAGES DIR>]

If optional parameters are not set then defaults will be used:
       For single image compression, compressed image will be created in same directory are input image and have '.compressed' file extension.
       For multiple image compression, compressed images will be created in a new 'out' sub directory of <INPUT IMAGES DIR> and have '.compressed' file extensions.
       For single image decompression, decompressed image will be created in same directory are input image and have '.out.png' file extension.
       For multiple image decompression, decompressed images will be created a new 'out' sub directory of <COMPRESSED IMAGE DIR> and have '.png' file extensions.

Bei der ersten Ausführung dieser Anwendung werden die erforderlichen Dateien generiert und in einem Verzeichnis gespeichert, das sich auf das Ausführungsarbeitsverzeichnis bezieht. Das kann ein paar minuten dauern. Für nachfolgende Ausführungen muss dieser Schritt nicht ausgeführt werden.


Das sieht toll aus. Nur um zu bestätigen, die Schritte 1-6 verlassen sich überhaupt nicht auf den Korpus? Würde es Ihnen auch etwas ausmachen, wenn ich Ihren Code stattdessen auf gist.github.com erneut hoste?
Orlp

Richtig, verwendet keine der Korpusdateien als Eingabe. Sie können die Bilder sehen, die es erzeugt, um die Patches zu generieren, indem Sie die Konstante "OUTPUT_SAMPLE_IMAGES" auf true setzen. Diese Bilder werden in den Arbeitsordner ausgegeben: data / images / working /
Moogie

@orlp jetzt mit gist.github
Moogie

Die Ergebnisse sind umwerfend, aber verstößt die Verwendung von Deflate / Inflate nicht gegen die Regel, generische Komprimierung / Dekomprimierung zu verbieten?
Algmyr

@algmyr Es ist schon eine Weile her, aber ich glaube, ich hatte die Regel "Keine generische Komprimierung" so interpretiert, dass sie keine generische "Bild" -Komprimierung bedeutet, dh JPEG usw. Aber ich habe das möglicherweise falsch interpretiert, in diesem Fall ja, dies Einreichung sollte diqualifiziert werden.
Moogie
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.