Определение качества JPG в Python (PIL)

Я играю с библиотекой PIL в Python, и мне интересно, как определить качество данного изображения в формате JPG. Я пытаюсь открыть изображение JPG, чтобы что-то сделать, и сохранить его снова в своем оригинальном качестве. Image.save позволяет мне определить желаемое качество:

im.save(name, quality = x)  

но я не вижу способа извлечь оригинал. На данный момент я просто догадываюсь и стараюсь иметь выходной файл того же размера, что и вход, выполняя двоичный поиск по параметру "качество", но это неприемлемое долгосрочное решение:)
Я также попытался использовать: Image.info, но большинство моих изображений не содержат никакой полезной информации (например: "adobe", "icc_profile", "exif", "adobe_transform" )
Помощь!

Ответы

Ответ 1

В PIL (и в основном все программные /librairies, которые используют libjpeg), параметр качества используется для построения таблицы квантования (ссылка). В libjpeg номер качества "масштабирует" значения примерной таблицы (из спецификации JPEG K.1). В других librairies там разные таблицы присваиваются различным качествам (например: Photoshop, цифровая камера).

Итак, в других терминах качество, равное таблице квантования, поэтому оно более сложное, чем просто число.

Если вы хотите сохранить свои модифицированные изображения с тем же "качеством", вам нужно будет использовать одну и ту же таблицу квантования. К счастью, таблица квантования встраивается в каждый JPEG. К сожалению, при сохранении в PIL невозможно указать таблицу квантования. cjpeg, утилиты командной строки, которые поставляются с libjpeg, могут это сделать.

Вот пример грубого кода, который сохраняет jpeg с указанной таблицей квантования:

from subprocess import Popen, PIPE
from PIL import Image, ImageFilter

proc = Popen('%s -sample 1x1 -optimize -progressive -qtables %s -outfile %s' % ('path/to/cjpeg', '/path/ta/qtable', 'out.jpg'), shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
P = '6'
if im.mode == 'L':
    P = '5'
stdout, stderr = proc.communicate('P%s\n%s %s\n255\n%s' % (P, im.size[0], im.size[1], im.tostring()))

Вам нужно будет найти способ извлечь таблицу квантования из исходного jpeg. djpeg может сделать это (часть libjpeg):

djpeg -verbose -verbose image.jpg > /dev/null

Вам также потребуется найти и установить выборку. Подробнее об этой проверке здесь. Вы также можете посмотреть test_subsampling

UPDATE

Я сделал вилку PIL, чтобы добавить возможность указывать таблицы подвыборки или квантования или как при сохранении JPEG. Вы также можете указать quality='keep' при сохранении, и изображение будет сохранено с помощью тех же таблиц квантования и подвыборки в качестве оригинала (оригинал должен быть JPEG). Также есть некоторые пресеты (на основе Photoshop), которые вы можете передать качеству при сохранении. Моя вилка.

ОБНОВЛЕНИЕ 2

Мой код теперь является частью Pillow 2.0. Так просто:

pip install Pillow

Ответ 2

Качество - это то, что используется для генерации данных, которые хранятся в формате JPEG. Этот номер не сохраняется в формате JPEG.

Один из способов определения качества - это взять верхнюю ячейку изображения размером 8x8 пикселей перед ее редактированием и запустить формулу сжатия JPEG только для того, чтобы приблизиться к оригиналу. Вам нужно разработать функцию расстояния от результата до оригинала (разность пикселей).

Вы по-прежнему будете выполнять качественный бинарный поиск, но это будет гораздо меньший объем работы.

Вот информация о том, как работает сжатие JPEG

https://www.dspguide.com/ch27/6.htm

Вот еще один способ из MS FAQ

https://support.microsoft.com/kb/324790

Вы должны перевести с С#.

Ответ 3

Я проверил ключевое слово quality = 'keep' в PIL 5.1. Он дает точно такой же результат, как и качество по умолчанию, равное 75.

from PIL import Image
img=Image.open('yy.jpg')
img.save('xx.jpg', quality='keep')
img.save('xx1.jpg', quality=75)
img.save('xx2.jpg') # no quality keyword so default is applied

import sh
for i, f in enumerate(('yy.jpg', 'xx1.jpg', 'xx2.jpg')):
    try:
        a = ('xx.jpg', f)
        r = sh.diff(*a)
    except sh.ErrorReturnCode as e:
        r = e.stdout
    r = r.rstrip()
    r = r if r else 'are the same'
    print i, a, r

Ответ 4

У меня были проблемы с использованием quality='keep' в сочетании с некоторыми операциями PIL, потому что, например, во время rotate() или transpose(), создается новый экземпляр Image, который теряет некоторые свойства, такие как format и quantization.

Мне пришлось искать источник Pillow, чтобы понять это, но, кажется, вы также можете сделать это так:

def _save_image_matching_quality(img, original_img, fp):
    frmt = original_img.format

    if frmt == 'JPEG':
        quantization = getattr(original_img, 'quantization', None)
        subsampling = JpegImagePlugin.get_sampling(original_img)
        quality = 100 if quantization is None else 0
        img.save(fp, format=frmt, subsampling=subsampling, qtables=quantization, quality=quality)
    else:
        img.save(fp, format=frmt, quality=100)

Он должен делать все, что делает quality='keep' :)

Этот точный код может не подходить для каждого варианта использования, и вам, возможно, придется настроить его. То, чего я пытался добиться, - это сэкономить как можно больше места, но не влияя на качество изображения как наивысший приоритет.

Для общего случая использования это может быть лучше:

def _save_image_matching_quality(img, original_img, fp):
    frmt = original_img.format

    if frmt == 'JPEG':
        quantization = getattr(original_img, 'quantization', None)
        subsampling = JpegImagePlugin.get_sampling(original_img)
        img.save(fp, format=frmt, subsampling=subsampling, qtables=quantization)
    else:
        img.save(fp, format=frmt)

Ответ 5

Насколько я понял тебя, это невозможно. Формат JPEG сжимается путем удаления 75% данных цвета или путем упрощения цветов. Невозможно повысить качество цвета.