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


30

У цьому виклику ви створите алгоритм стиснення попереднього перегляду зображення. Його мета полягає в тому, щоб зменшити довільний файл зображення до попереднього зображення на 4 KiB, який можна використовувати для швидкого визначення зображень із дуже малою пропускною здатністю.

Ви повинні написати дві програми (або одну комбіновану програму): компресор і декомпресор. Обидва повинні приймати файл або stdin як вхідні дані та виводити їх у файл чи stdout. Компресор повинен приймати одне зображення у форматі обраного зображення без втрат (наприклад, 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)))

Виграє найнижчий рахунок.


чи має бути стислий малюнок для перегляду?
Евмель

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 У правилах зазначено, що декомпресору потрібно декодирувати менше десяти секунд. Моя ідея потенційно потребуватиме декількох хвилин, щоб генерувати довідкові файли, які будуть використовуватися при наступних викликах декомпресора. тобто це одноразова діяльність, схожа на "встановлення" програми. Чи було б це рішення дискваліфіковано?
Мугі

Відповіді:


8

Пітон з 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 KiB і має таке ж співвідношення сторін, як оригінал, і використовувати фільтр Lanczos, щоб отримати максимально високу якість з зображених зображень, що знімаються.

Імгур альбом стислих зображень після зміни оригінальних розмірів

Оцінка виводу сценарію:

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, який є нестисненим форматом.
Mego

Добре. Цей виклик все ж виявляє досить слабку слабкість DSSIM. Я б заперечував, що більшість зображень Мугі виглядають істотно краще.
orlp

@orlp Вони добре виглядають як ескізи. Коли вони підірвані до своїх початкових розмірів (використовуючи Lanczos), вони виглядають приблизно однаково якісно або гірше. Я працюю над завантаженням мініатюр мого результату.
Mego

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.

Для декомпресії надуйте, а потім прочитайте індекси блоків та виведіть відповідний патч у вихідний файл, а потім змініть розмір до початкових розмірів.

На жаль, код занадто великий для stackoverflow, тому його можна знайти за посиланням 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" на справжню. Він виведе ці зображення в робочу папку: data / images / working /
Moogie

@orlp тепер використовує gist.github
Moogie

Результати приголомшливі, але хіба за допомогою дефляції / надуття не порушується правило про заборону загального стиснення / декомпресії?
альгмір

@algmyr Минув час, але я думаю, що я інтерпретував не загальне правило стиснення як таке, що не означає загального стиснення "зображення" ... тобто jpeg і т. д. Але я, можливо, інтерпретував це неправильно, у такому випадку, так, це подання має бути кваліфіковано.
Moogie
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.