Kompres gambar ke pratinjau 4 KiB

30

Dalam tantangan ini, Anda akan membuat algoritma kompresi pratinjau gambar. Tujuannya adalah untuk mengurangi file gambar sewenang-wenang menjadi gambar pratinjau 4 KiB, yang dapat digunakan untuk dengan cepat mengidentifikasi gambar dengan bandwidth yang sangat sedikit.

Anda harus menulis dua program (atau satu program gabungan): kompresor dan dekompresor. Keduanya harus mengambil file atau stdin sebagai input, dan output ke file atau stdout. Kompresor harus menerima satu gambar dalam format gambar utama lossless pilihan (mis. PNG, BMP, PPM), dan menghasilkan file paling banyak 4.096 byte . Dekompresor harus menerima file apa pun yang dihasilkan oleh kompresor, dan mengeluarkan gambar sedekat mungkin ke input. Perhatikan bahwa tidak ada batasan ukuran kode sumber untuk enkoder / decoder, sehingga Anda dapat kreatif dalam algoritme Anda.

Pembatasan:

  • Tidak 'curang'. Program-program Anda mungkin tidak menggunakan input tersembunyi, menyimpan data di internet, dll. Anda juga dilarang memasukkan fitur / data yang hanya berkaitan dengan set gambar penilaian.

  • Untuk perpustakaan / tools / built-in Anda yang diperbolehkan untuk menggunakan generik operasi pengolahan citra (scaling, mengaburkan, warna ruang transformasi, dll), tetapi tidak gambar decoding / encoding / kompresi operasi (kecuali untuk masukan kompresor dan output decompressor). Kompresi / dekompresi generik juga tidak diizinkan . Ini dimaksudkan agar Anda menerapkan kompresi Anda sendiri untuk tantangan ini.

  • Dimensi output gambar oleh dekompresor harus persis sama dengan yang ada pada file asli yang diberikan ke kompresor. Anda dapat mengasumsikan bahwa dimensi gambar tidak melebihi 2 16 di kedua arah.

  • Kompresor Anda harus dijalankan pada PC konsumen rata-rata dalam waktu kurang dari 5 menit, dan dekompresor harus berjalan dalam waktu di bawah 10 detik untuk gambar apa pun dalam set di bawah ini.

Mencetak gol

Untuk membantu verifikasi cepat dan perbandingan visual, harap sertakan album gambar lossless test corpus setelah kompresi menggunakan jawaban Anda.

Kompresor Anda akan diuji menggunakan kumpulan gambar berikut :

penuh bintang sumber kamar Pelangi
batas llama anak julia

Anda dapat mengunduh semua gambar dalam file zip di sini .

Skor Anda akan menjadi indeks kesamaan struktural rata-rata untuk kompresor Anda di semua gambar. Kami akan menggunakan sumber terbuka dssimuntuk tantangan ini. Itu mudah dibangun dari sumber, atau jika Anda berada di Ubuntu itu juga memiliki PPA. Lebih disukai jika Anda skor jawaban Anda sendiri, tetapi jika Anda tidak tahu bagaimana membangun aplikasi C dan Anda tidak menjalankan Debian / Ubuntu, Anda dapat membiarkan orang lain mencetak skor untuk Anda. dssimmengharapkan input / output dalam PNG, jadi konversikan output Anda ke PNG terlebih dahulu jika Anda output dalam format yang berbeda.

Untuk membuat skor tidak menyakitkan, berikut ini skrip Python penolong cepat, penggunaan 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)))

Skor terendah menang.

orlp
sumber
apakah gambar yang dikompres harus dapat dilihat?
Eumel
2
@Eumel Anda dapat mempertimbangkan dekompresor sebagai pemirsa. Jadi tidak, format terkompresi Anda dapat berubah-ubah dan sepenuhnya terserah Anda. Hanya setelah dekompresi gambar yang terlihat harus keluar.
orlp
7
You may assume that the image dimensions do not exceed 2^32 in either direction.Bukankah ini sedikit berlebihan? Ini berarti saya harus menggunakan hingga 16 byte untuk menyimpan sepasang koordinat (x, y). Beberapa file gambar memiliki dimensi lebih dari 2 ^ 16 (65536) piksel di kedua arah, dan 2 ^ 11 cukup untuk semua gambar di dalam corpus.
Peter Olson
@PeterOlson saya akan mengubahnya menjadi 2^16.
orlp
@ orlp Aturan menyatakan bahwa dekompresor harus kurang dari sepuluh detik untuk mendekode gambar. Gagasan yang saya miliki akan berpotensi memakan waktu beberapa menit untuk menghasilkan file referensi yang akan digunakan dalam panggilan berikutnya ke dekompresor. yaitu itu adalah aktivitas yang sama sekali mirip dengan "menginstal" aplikasi. Apakah solusi ini akan didiskualifikasi?
Moogie

Jawaban:

8

Python dengan PIL, skor 0,094218

Kompresor:

#!/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

Dekompresor:

#!/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

Kedua skrip mengambil input melalui argumen baris perintah, sebagai dua direktori (input dan output), dan mengonversi semua gambar di direktori input.

Idenya adalah untuk menemukan ukuran yang pas di bawah 4 KiB dan memiliki rasio aspek yang sama seperti aslinya, dan menggunakan filter Lanczos untuk mendapatkan kualitas terbaik dari gambar downsampled mungkin.

Imgur album gambar terkompresi, setelah mengubah ukuran ke dimensi asli

Mencetak keluaran skrip:

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
Mego
sumber
Saya baru menyadari bahwa solusi Anda menggunakan WebP, yang tidak diizinkan. Solusi Anda tidak valid.
orlp
@orlp Sekarang sudah diperbaiki untuk menggunakan PPM, yang merupakan format yang tidak terkompresi.
Mego
Baik. Tantangan ini memang mengekspos kelemahan DSSIM sedikit. Saya berpendapat bahwa sebagian besar gambar Moogie terlihat jauh lebih baik.
orlp
@ orlp Mereka terlihat bagus sebagai thumbnail. Ketika diledakkan ke dimensi aslinya (menggunakan Lanczos), mereka terlihat memiliki kualitas yang sama atau lebih buruk. Saya sedang berupaya untuk mendapatkan thumbnail dari output saya yang diunggah.
Mego
7

Java (vanilla, harus bekerja dengan java 1.5+), skor 0.672

Ini tidak menghasilkan skor DSSIM yang sangat baik tetapi, bagi saya itu menghasilkan gambar yang lebih ramah manusia ...

penuh bintang sumber kamar Pelangi
batas llama anak julia

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

Mencetak keluaran skrip:

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

Rantai kompresi:

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.

Untuk mendekompres, mengembang dan kemudian membaca indeks blok dan output tambalan yang sesuai ke file output, kemudian mengubah ukuran ke dimensi asli.

Sayangnya kode ini terlalu besar untuk stackoverflow sehingga dapat ditemukan di https://gist.github.com/anonymous/989ab8a1bb6ec14f6ea9

Untuk berlari:

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.

Pertama kali aplikasi ini dijalankan, file-file yang diperlukan akan dihasilkan dan disimpan dalam direktori relatif terhadap eksekusi yang berfungsi dir. Ini mungkin memakan waktu beberapa menit. Untuk eksekusi selanjutnya, langkah ini tidak perlu dilakukan.

Moogie
sumber
Ini terlihat luar biasa. Sekedar konfirmasi, langkah 1-6 tidak mengandalkan corpus sama sekali? Juga, apakah Anda keberatan jika saya memasang kembali kode Anda di gist.github.com?
orlp
Benar, tidak menggunakan salah satu file corpus sebagai input. Anda dapat melihat gambar yang dihasilkannya untuk menghasilkan pembelian patch yang mengubah konstanta "OUTPUT_SAMPLE_IMAGES" menjadi true. Ini akan menampilkan gambar-gambar ini ke folder kerja: data / images / working /
Moogie
@orlp sekarang menggunakan gist.github
Moogie
Hasilnya menakjubkan, tetapi tidak menggunakan deflate / inflate melanggar aturan tentang pembatalan kompresi / dekompresi generik?
algmyr
@ algmyr Sudah lama, tapi saya pikir saya telah menafsirkan aturan kompresi tidak umum sebagai berarti tidak ada kompresi 'gambar' generik ... yaitu jpeg, dll. Tapi saya mungkin telah menafsirkannya secara tidak benar, dalam hal ini, ya, ini pengajuan harus didiskualifikasi.
Moogie