Определение качества JPG в Python (PIL) - PullRequest
18 голосов
/ 04 декабря 2010

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

im.save(name, quality = x)  

, но я не вижу способа извлечь оригинал.Сейчас я просто угадываю и пытаюсь получить выходной файл того же размера, что и входные данные, выполняя бинарный поиск по параметру 'quality', но это недопустимое долгосрочное решение :)большинство моих изображений там не содержат никакой полезной информации (например: 'adobe', 'icc_profile', 'exif', 'adobe_transform')
Help!

Ответы [ 5 ]

26 голосов
/ 04 декабря 2010

В PIL (и в основном во всех программах / библиотеках, использующих libjpeg ) параметр качества используется для построения таблицы квантования ( ref. ). В libjpeg качественное число «масштабирует» значения таблицы сэмплов (из спецификации JPEG Раздел K.1). В других библиотеках разные таблицы присваиваются разным качествам (например, 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

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

pip install Pillow
4 голосов
/ 04 декабря 2010

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

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

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

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

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

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

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

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

0 голосов
/ 19 июня 2019

У меня были проблемы с использованием 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)
0 голосов
/ 02 мая 2018

Я проверил ключевое слово 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
0 голосов
/ 04 декабря 2010

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

...