Настройка плотности JPG (dpi) без увеличения размера файла

Я использую (под Windows) следующую команду

magick convert -units pixelsperinch file_in -density 600 file_out

для установки dpi (без повторной выборки, поскольку dpi в основном, насколько я понимаю, просто тег, определяющий размер пикселя) изображения JPG. Это работает, но я не понимаю, почему он увеличивает размер файла на несколько килобайт (мой образ, первоначально 1658 КБ, стал 1717 КБ, что составляет увеличение на 59 КБ), тогда как я ожидал бы увеличения, если таковое имеется, всего несколько байт.

Я что-то не так понял? Можно ли изменить с помощью командной строки (также приветствуются инструменты, отличные от ImageMagick) плотность / dpi JPG без увеличения размера файла?

Заранее спасибо за любую подсказку.


person mmj    schedule 05.08.2018    source источник
comment
См. ответ Марка Сетчелла ниже. Причина размера файла заключается в том, что Imagemagick будет распаковывать и повторно сжимать ваш JPG. Это само по себе может изменить размер файла, но особенно если ваш входной JPG не указывает его качество, поэтому Imagemagick назначает и повторно сжимает с высоким уровнем качества 92. Различные таблицы сжатия JPG между входом и выходом также могут вызывать различия, а также разные факторы выборки. Поэтому вы должны проверить ввод и вывод с помощью magick identify -verbose image.jpg. Также обратите внимание, что в Imagemagick 7 используйте только магию, а не преобразование или преобразование магии.   -  person fmw42    schedule 05.08.2018
comment
Если вы заархивируете свои входные и выходные изображения и разместите их в другом месте, а затем опубликуете ссылку, чтобы этот форум не перекодировал их и потенциально не изменил изображения, мы могли бы просмотреть метаданные вашего изображения, используя magick identify -verbose image.jpg   -  person fmw42    schedule 05.08.2018
comment
@fmw42 Вот ссылки на изображение до и после изменения dpi: drive.google.com/open ?id=1vK9JSH3CaCeufrmozJE_KWGAB3MNnX3Q drive.google.com/open?id=1DW7ATCgWczVCHzIKjRcU9XctEKtoamut ~ Также обратите внимание, что если я оптимизирую изображение с помощью jpegran, исходное значение dpi будет потеряно.   -  person mmj    schedule 06.08.2018
comment
@fmw42 Дополнительные тесты показывают, что если я изменяю dpi с помощью Image Magick, размер файла увеличивается, а dpi теряется после оптимизации без потерь jpegtran, тогда как если я изменяю dpi с помощью Irfanview (из командной строки), файл уменьшается в размере (вероятно, потому что Irfanview преобразует цветовое пространство из CMYK в sRGB) и dpi НЕ теряется после jpegtran без потерь оптимизация.   -  person mmj    schedule 06.08.2018
comment
@mmj Как я уже сказал выше, Imagemagick будет распаковывать и повторно сжимать, даже если вы просто измените dpi. Итак, вы хотите использовать какой-то другой инструмент, который этого не делает. Есть много причин, по которым размер файла изменится при распаковке и повторном сжатии JPG.   -  person fmw42    schedule 06.08.2018


Ответы (4)


Вы можете изменить/установить плотность без повторного кодирования файла (и тем самым, возможно, изменения его размера или качества) с помощью гораздо меньшего, легкого и простого в установке exiftool, который "просто" Perl сценарий:

exiftool -jfif:Xresolution=300 -jfif:Yresolution=300 YourImage.jpg

Разные люди называют разные вещи плотностью/разрешением, поэтому, если приведенная выше команда не делает то, что вы хотите/нужно/надеетесь/ожидаете, возможно, попробуйте:

exiftool -XResolution=300 -YResolution=300 YourImage.jpg
person Mark Setchell    schedule 05.08.2018
comment
exiftool выглядит многообещающе, но, к сожалению, с моим изображением я получил Warning: Not creating JFIF in JPEG with Adobe APP14. Конечно, это ошибка моего изображения (похоже, это Adobe JPG, а не JFIF JPG), тем не менее я смог изменить dpi такого изображения без увеличения размера, используя Irfanview с помощью командной строки: i_view32.exe file_in.jpg /dpi=(600,600) /convert=file_out.jpg . Irfanview выполняет некоторое кодирование/декодирование, но может работать с AdobeJPG, и результирующий файл еще меньше. - person mmj; 06.08.2018
comment
Дополнительные тесты показывают, что Irfanview преобразует цветовое пространство из CMYK в sRGB, что, по-видимому, уменьшает размер файла и позволяет оптимизировать без потерь с jpegtran сохраняя информацию о dpi. (см. также мои комментарии к вопросу). - person mmj; 06.08.2018

Пытаюсь воспроизвести вашу проблему:

  • Чересстрочный / прогрессивный JPEG Gimp меньше, чем версия без чересстрочной развертки (также производимая Gimp)
  • Выходной JPEG magick convert не чересстрочный, и на самом деле он немного меньше, чем собственный не чересстрочный формат Gimp. Этот файл имеет одинаковый размер независимо от того, создан ли он из чересстрочной или не чересстрочной развертки.

Итак, я думаю, что вы конвертируете чересстрочный/прогрессивный JPEG. Обратите внимание, что это также демонстрирует, что IM перекодирует файл, и сравнение исходного и перекодированного в Gimp показывает существенные различия.

В формате JPEG определения H/V закодированы в 4 байта в заголовке, исправление, которое должно быть SMOP на любом языке программирования.

person xenoid    schedule 05.08.2018
comment
Можете ли вы дать некоторую (простую) ссылку на эти байты в заголовке, чтобы я мог попытаться написать фрагмент? - person mmj; 05.08.2018
comment
См. Википедию (APP0 XDensity/YDensity). - person xenoid; 05.08.2018

Попробуйте использовать https://convert.town/image-dpi. На моем JFIF jpg это только изменило DPI, а не передискретизировало изображение. Другие предложения по программному обеспечению можно найти в этом вопросе суперпользователя, но это было единственное, что сработало для меня без каких-либо изменений в образе.

Однако у меня есть только jpg JFIF, а у вас, похоже, есть Adobe jpg из других ваших комментариев, поэтому ваш пробег может отличаться.

Я проверил изменение DPI и отсутствие повторной выборки с помощью шестнадцатеричного редактора Windows HxD, просто взглянув на 4 байта, подробно описанные в статье Википедии, на которую ссылается xenoid. И как только вы узнаете, какие байты меняются, вы можете пропустить веб-сайт и использовать шестнадцатеричный редактор напрямую, чтобы установить X и Y DPI.

person Micah Lindstrom    schedule 11.12.2019
comment
Хороший инструмент, спасибо за совет, но мне нужен инструмент командной строки Windows, а не онлайн-инструмент. - person mmj; 12.12.2019
comment
Они предлагают платную версию для Windows по адресу convert.town/downloaddpi. Это не командная строка, но, учитывая, что они хотят, чтобы вы попросили их версии для Mac/Linux, я уверен, что они были бы рады сделать версию для командной строки для Windows. Если вы не хотите платить за это, единственным оставшимся вариантом может быть создание собственного сценария. Я предполагаю, что вы могли бы написать довольно простой скрипт Python для вашего случая, предполагая, что все ваши JPG исходят из одной и той же камеры/программного обеспечения и, таким образом, все они могут редактировать свой шестнадцатеричный код одинаково. - person Micah Lindstrom; 17.03.2020

Насколько я понимаю, существует несколько разновидностей файлов JPEG . Детали, необходимые для изменения метаданных DPI варианта JFIF, объясняются здесь. Основываясь на этом, я написал свой собственный Python-скрипт, который позволяет изменить настройку DPI для разновидности JFIF JPEG без перекодирования:

import sys,os

filename = sys.argv[1]
x_density = int(sys.argv[2])
y_density = int(sys.argv[3])

echo = True
if len(sys.argv) > 4:
    if sys.argv[4] == 'quiet':
        echo = False

assert x_density > 0 and x_density < 65536
assert y_density > 0 and y_density < 65536

# JPEG markers
APP0 = bytes.fromhex('FFD8FFE0') # JFIF
APP1 = bytes.fromhex('FFD8FFE1') # EXIF

with open(filename, 'rb+') as f: # 'w+b'
    chunk = f.read(4)
    if chunk == APP0:
        f.seek(2,1) # relative seek
        chunk = f.read(5)
        if chunk == bytes.fromhex('4A46494600'): # JFIF indentfier
            f.seek(2,1) # relative seek
            print('Setting density of ' + os.path.split(filename)[1] + ' to ' + str(x_density) + 'x' + str(y_density) + ' dpi.') if echo else None
            f.write(bytes.fromhex('01'))
            f.write(x_density.to_bytes(2,'big'))
            f.write(y_density.to_bytes(2,'big'))
        else:
            print('File hasn''t got the JFIF indentifier, nothing was done.')
    elif chunk == APP1:
        f.close() # needed otherwise exiftool cannot operate on file
        print('This is an EXIF-JPEG, using exiftool to set DPI...')
        os.system('exiftool -P -overwrite_original -XResolution={} -YResolution={} "{}"'.format(x_density,y_density,filename))
    else:
        print('File is not JFIF nor EXIF, cannot set DPI, nothing was done.')

print('Done.') if echo else None

Применение:

python this_script.py some-image.jpg Xdpi Ydpi [quiet]

Скрипт не читает полное изображение и не изменяет длину файла, он просто изменяет несколько байтов непосредственно в файле JPEG. Кроме того, временная/резервная копия не создается, потому что я хотел, чтобы скрипт работал с жестко связанными файлами, поэтому в целом процесс для JFIF JPEG выполняется довольно быстро.

Скрипт может идентифицировать EXIF ​​JPEG и использовать exiftool для изменения DPI. Если на вашем компьютере не установлен exiftool, не забудьте соответствующим образом настроить скрипт. Причиной использования этого скрипта, даже если у вас установлен exiftool, является скорость; в моем тесте этот скрипт намного быстрее, чем exiftool.

JFIF и EXIF ​​являются наиболее распространенными разновидностями файлов JPEG, но я надеюсь, что кто-то сможет улучшить этот сценарий или сообщить способ установки DPI (без перекодирования) также для файлов Adobe JPEG с маркером APP14, которые не так уж редки.

person mmj    schedule 02.06.2021