【问题标题】:Setting JPG density (dpi) with no increase in file size在不增加文件大小的情况下设置 JPG 密度 (dpi)
【发布时间】:2019-01-12 13:41:23
【问题描述】:

我使用(在 Windows 下)以下命令

magick convert -units pixelsperinch file_in -density 600 file_out

设置JPG图像的dpi(不重新采样,据我所知,dpi基本上是一个指定像素大小的标签)。它有效,但我不明白为什么它将文件大小增加了几千字节(我最初的 1658 kB 的图像变成了 1717 kB,增加了 59 kB),而我希望增加(如果有的话)只有几个字节。

我是不是搞错了什么?是否可以在不增加文件大小的情况下通过命令行(也欢迎使用 ImageMagick 以外的工具)更改 JPG 的密度/dpi?

提前感谢任何线索。

【问题讨论】:

  • 请参阅下面的 Mark Setchell 的回答。文件大小发生的原因是 Imagemagick 将解压缩并重新压缩您的 JPG。仅此一项可能会改变文件大小,但尤其是如果您的输入 JPG 未指定其质量,因此 Imagemagick 会以 92 的高质量级别分配和重新压缩。输入和输出之间的不同 JPG 压缩表也可能导致差异和不同采样因子。所以你应该用magick identify -verbose image.jpg检查输入和输出。另请注意,在 Imagemagick 7 中,仅使用 magick,而不是 magick convert 或 convert。
  • 如果您压缩您的输入和输出图像并将它们托管在其他地方,然后发布链接以便本论坛不会重新编码它们并可能更改图像,那么我们可以查看您的图像元数据使用magick identify -verbose image.jpg
  • @fmw42 以下是dpi更改前后的图片链接:drive.google.com/open?id=1vK9JSH3CaCeufrmozJE_KWGAB3MNnX3Qdrive.google.com/open?id=1DW7ATCgWczVCHzIKjRcU9XctEKtoamut~另外注意如果我用jpegran优化图片,原来的dpi 丢失。
  • @fmw42 更多测试表明,如果我使用 Image Magick 更改 dpi,则在进行 jpegtran 无损优化后,文件大小会增加并且 dpi 会丢失,而如果我用 Irfanview (通过命令行)更改 dpi,文件会减小(可能是因为 Irfanview 将色彩空间从 CMYK 转换为 sRGB)并且 dpi 在 之后不会丢失b>jpegtran 无损优化。
  • @mmj 正如我上面所说的,即使您只是更改 dpi,Imagemagick 也会解压缩并重新压缩。所以你想使用其他一些不这样做的工具。 JPG解压再压缩时文件大小会发生变化的原因有很多。

标签: image imagemagick jpeg pixel-density


【解决方案1】:

尝试重现您的问题:

  • Gimp 的隔行/逐行 JPEG 小于非隔行版本(也由 Gimp 制作)
  • magick convert 输出的 JPEG 不是隔行扫描的,实际上比 Gimp 自己的非隔行扫描的要小一些。无论是隔行扫描还是非隔行扫描版本,该文件的大小都是相同的。

所以,我认为您正在转换隔行/逐行 JPEG。请注意,这也表明 IM 正在重新编码文件,并且在 Gimp 中比较原始文件和重新编码后的文件显示出显着差异。

在 JPEG 格式中,H/V 定义在标头中编码为 4 个字节,补丁应该是任何编程语言中的 SMOP。

【讨论】:

  • 您能否对标头中的这些字节提供一些(简单)参考,以便我可以尝试编写 sn-p?
  • Wikipedia (APP0 XDensity/YDensity)。
【解决方案2】:

您可以通过更小、更轻且更易于安装的exiftool 来更改/设置密度无需重新编码文件(从而可能更改其大小或质量) “只是”一个 Perl 脚本:

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

不同的人把不同的东西称为密度/分辨率,所以如果上面的命令没有达到你想要/需要/希望/期望的效果,不妨试试:

exiftool -XResolution=300 -YResolution=300 YourImage.jpg

【讨论】:

  • exiftool 看起来很有希望,但不幸的是,我得到了Warning: Not creating JFIF in JPEG with Adobe APP14。当然这是我的图像错误(它似乎是 Adob​​e JPG 而不是 JFIF JPG),但是我能够通过命令行使用 Irfanview 更改此类图像的 dpi 而不会增加大小:@ 987654325@。 Irfanview 做了一些编码/解码,但能够处理 Adob​​eJPG 并且生成的文件更小。
  • 更多测试表明 Irfanview 将色彩空间从 CMYK 转换为 sRGB,这似乎可以减小文件大小并允许使用 jpegtran 进行无损优化 保留 dpi信息(另请参阅我的cmets问题)。
【解决方案3】:

尝试使用the online tool https://convert.town/image-dpi。在我的 JFIF jpg 上,这只改变了 DPI,并没有重新采样图像。其他软件建议可以在 on this Super User question 找到,但这是唯一对我有用的软件,无需更改图像中的任何其他内容。

但是,我只有 JFIF jpg,而您似乎拥有其他 cmets 的 Adob​​e jpg,因此您的里程可能会有所不同。

我使用 Windows 十六进制编辑器 HxD 验证了 DPI 更改并且没有重新采样,只是查看了 xenoid 链接的 Wikipedia 文章中详述的 4 个字节。一旦您知道哪些字节正在发生变化,您就可以跳过网站并直接使用十六进制编辑器来设置 X 和 Y DPI。

【讨论】:

  • 不错的工具,感谢您的建议,但我需要一个 Windows 命令行工具,而不是在线工具。
  • 他们在convert.town/downloaddpi 提供付费 Windows 版本。这不是命令行,但考虑到他们希望您向他们询问 Mac/Linux 版本,我相信他们也很乐意制作 Windows 命令行版本。如果您不想为此付费,剩下的唯一选择可能是制作自己的脚本。我的猜测是你可以为你的案例编写一个非常简单的 Python 脚本,假设你的 JPG 都来自同一个相机/软件,因此都可以用相同的方式编辑它们的十六进制代码。
【解决方案4】:

据我了解,有several flavors of JPEG files。需要的详细信息change DPI metadata of JFIF flavor are explained here。基于此,我编写了自己的 Python 脚本,该脚本允许在不重新编码的情况下更改 JFIF 风格 JPEG 的 DPI 设置:

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 文件风格,但我希望有人能够改进此脚本或报告一种为带有 APP14 标记的 Adob​​e JPEG 设置 DPI(无需重新编码)的方法,这并不罕见。

【讨论】:

    猜你喜欢
    • 2014-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-27
    • 2021-12-25
    • 1970-01-01
    • 2014-09-01
    相关资源
    最近更新 更多