Сжатие изображения для предварительного просмотра 4 КиБ


30

В этом задании вы будете создавать алгоритм сжатия предварительного просмотра изображений. Его цель состоит в том, чтобы уменьшить произвольный файл изображения до изображения предварительного просмотра 4 КиБ, которое можно использовать для быстрой идентификации изображений с очень небольшой пропускной способностью.

Вы должны написать две программы (или одну комбинированную программу): компрессор и декомпрессор. Оба должны принимать файл или стандартный ввод в качестве входных данных и выводить их в файл или стандартный вывод. Компрессор должен принимать одно изображение в основном формате изображения без потерь (например, PNG, BMP, PPM) и выводить файл размером не более 4096 байт . Декомпрессор должен принимать любой файл, сгенерированный компрессором, и выводить изображение как можно ближе к входу. Обратите внимание, что для кодера / декодера нет ограничения по размеру исходного кода, поэтому вы можете проявить изобретательность в своем алгоритме.

Ограничения:

  • Без обмана'. Ваши программы не могут использовать скрытые входы, хранить данные в Интернете и т. Д. Вам также запрещается включать функции / данные, относящиеся только к набору изображений для оценки.

  • Для библиотек / инструментов / встроенных модулей вы которые разрешено использовать общие операции обработки изображения (масштабирование, размытие, цветовое пространство преобразования, и т.д.), но не изображения декодирования / кодирования / сжатия операций (для ввода компрессора и декомпрессора выход за исключением). Общее сжатие / декомпрессия также не разрешено . Предполагается, что вы реализуете собственное сжатие для этой задачи.

  • Размеры изображения, выводимого декомпрессором, должны точно соответствовать размерам исходного файла, переданного компрессору. Вы можете предположить, что размеры изображения не превышают 2 16 в любом направлении.

  • Ваш компрессор должен работать на обычном потребительском ПК менее чем за 5 минут, а декомпрессор должен работать менее чем за 10 секунд для любого изображения в наборе ниже.

счет

Чтобы помочь быстрой проверке и визуальному сравнению, включите альбом с изображениями без потерь тестового корпуса после сжатия, используя свой ответ.

Ваш компрессор будет протестирован с использованием следующего набора изображений :

звездный источник номер радуга
прибыль гуанако дитя Юлия

Вы можете скачать все изображения в виде zip-файла здесь .

Ваша оценка будет средним индексом структурного сходства вашего компрессора на всех изображениях. Мы будем использовать открытый исходный код dssimдля этой задачи. Он легко собирается из исходного кода или, если вы используете Ubuntu, у него также есть PPA. Желательно, чтобы вы набрали собственный ответ, но если вы не знаете, как создавать приложения на C и не запускаете Debian / Ubuntu, вы можете позволить кому-то другому заняться за вас. dssimожидает ввода / вывода в формате PNG, поэтому сначала преобразуйте вывод в формат PNG, если вы выводите в другом формате.

Чтобы сделать скоринг безболезненным, вот быстрый вспомогательный скрипт 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)))

Самый низкий балл побеждает.


Должна ли сжатая картинка быть видимой?
Eumel

2
@Eumel Вы можете рассматривать декомпрессор как зрителя. Так что нет, ваш сжатый формат может быть произвольным и полностью зависит от вас. Только после распаковки должно появиться видимое изображение.
orlp

7
You may assume that the image dimensions do not exceed 2^32 in either direction.Разве это не немного чрезмерно? Это означает, что я должен использовать до 16 байтов для хранения пары (x, y) координат. Немногие файлы изображений имеют размеры более 2 ^ 16 (65536) пикселей в любом направлении, и 2 ^ 11 достаточно для всех изображений в корпусе.
Питер Олсон

@PeterOlson Я поменяю его на 2^16.
orlp

@orlp Правила гласят, что декомпрессору требуется менее десяти секунд для декодирования изображения. Идея, которая у меня есть, потенциально займет несколько минут для генерации справочных файлов, которые будут использоваться при последующих вызовах декомпрессора. то есть это разовое действие, похожее на «установку» приложения. Будет ли это решение дисквалифицировано?
Moogie

Ответы:


8

Python с PIL, оценка 0.094218

Компрессор:

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

декомпрессор:

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

Оба сценария принимают входные данные через аргументы командной строки в виде двух каталогов (входной и выходной) и преобразуют все изображения во входном каталоге.

Идея состоит в том, чтобы найти размер, который подходит под 4 КиБ и имеет то же соотношение сторон, что и оригинал, и использовать фильтр Ланцоша, чтобы получить максимально высокое качество изображения с пониженной дискретизацией.

Imgur альбом сжатых изображений, после изменения размера до исходного размера

Вывод сценария оценки:

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

Я только что понял, что ваше решение использует WebP, что запрещено. Ваше решение неверно.
orlp

@orlp Исправлено использование PPM, который является несжатым форматом.
Мего

Хорошо. Эта проблема действительно демонстрирует слабость DSSIM. Я бы сказал, что большинство изображений Муги выглядят значительно лучше.
orlp

@orlp Они выглядят как эскизы. При увеличении до первоначальных размеров (с использованием Lanczos) они выглядят примерно одинаково или хуже. Я работаю над тем, чтобы загружать эскизы выходных данных.
Мего

7

Java (ваниль, должна работать с Java 1.5+), оценка 0,672

Он не дает особенно хороших результатов dssim, но, на мой взгляд, он создает более дружественные для человека изображения ...

звездный источник номер радуга
прибыль гуанако дитя Юлия

Альбом: http://imgur.com/a/yL31U

Вывод сценария оценки:

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

Цепочка сжатия:

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.

Чтобы распаковать, надуть, а затем прочитать индексы блоков и вывести соответствующий патч в выходной файл, а затем изменить размер до исходных размеров.

К сожалению, код слишком велик для стекового потока, поэтому его можно найти по адресу https://gist.github.com/anonymous/989ab8a1bb6ec14f6ea9.

Бежать:

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.

При первом запуске этого приложения необходимые файлы будут сгенерированы и сохранены в каталоге, соответствующем рабочему каталогу выполнения. Это может занять несколько минут. Для последующих исполнений этот шаг выполнять не нужно.


Это выглядит потрясающе. Просто чтобы подтвердить, шаги 1-6 не полагаются на корпус вообще? Кроме того, вы не возражаете, если я вместо этого перенесу ваш код на gist.github.com?
orlp

Правильно, не использует ни один из файлов корпуса в качестве входных данных. вы можете увидеть изображения, которые он создает для генерации патчей, меняя константу "OUTPUT_SAMPLE_IMAGES" на true. Он выведет эти изображения в рабочую папку: data / images / working /
Moogie

@orlp теперь использует gist.github
Moogie

Результаты ошеломляющие, но разве использование deflate / inflate не нарушает правило о запрете общего сжатия / распаковки?
Альгмир

@algmyr Это было какое-то время, но я думаю, что я интерпретировал правило отсутствия универсального сжатия как означающее не сжатие универсального «изображения» ... то есть jpeg и т. д. Но я, возможно, интерпретировал это неправильно, в этом случае, да, это представление должно быть переквалифицировано.
Moogie
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.