不增加文件大小的情况下设置JPG密度(dpi)

4
我(在Windows下)使用以下命令:
magick convert -units pixelsperinch file_in -density 600 file_out

设置JPG图像的dpi(不进行重采样,因为就我所理解的而言,dpi基本上只是指定像素大小的标记)。它起作用了,但我不明白为什么它会使文件大小增加几千字节(我的一张图片最初为1658 kB,现在变成了1717 kB,增加了59 kB),而我期望的是即使有增加,也只有几个字节。

我错了吗?是否可能通过命令行更改JPG文件的密度/ dpi而不增加文件大小?(欢迎使用ImageMagick之外的其他工具)

感谢您提前为任何线索。


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

5

你可以使用更小、更轻、更易安装的 Perl 脚本工具 exiftool无需重新编码文件(因此可能改变其大小或质量)即可更改/设置其密度。

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 的警告。虽然这似乎是我的图像问题(它似乎是 Adobe JPG 而不是 JFIF JPG),但我仍然能够使用 Irfanview 命令行更改此图像的 dpi 而不增加大小:i_view32.exe file_in.jpg /dpi=(600,600) /convert=file_out.jpg。 Irfanview 进行了一些编码/解码,但能够处理 AdobeJPG,并且生成的文件甚至更小。 - mmj
更多的测试表明,Irfanview 可以将颜色空间从 CMYK 转换为 sRGB,这似乎可以减小文件大小,并且允许使用 jpegtran 进行无损优化并保留 dpi 信息(请参见我的评论)。 - mmj

3

尝试重现您的问题:

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

因此,我认为您正在转换逐行扫描的JPEG。请注意,这也证明了IM正在重新编码文件,并且在Gimp中比较原始文件和重新编码后的文件会显示出显著的差异。

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


你能给一些关于头部中那些字节的(简单)参考吗?这样我就可以尝试编写一小段代码了。 - mmj
1
请参阅Wikipedia(APP0 XDensity/YDensity)。 - xenoid

0
据我所知,JPEG文件有几种不同的类型。这里解释了更改JFIF类型DPI元数据所需的详细信息(点击此处)。基于此,我编写了自己的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文件最常见的类型,但我希望有人能够改进这个脚本或报告一种设置DPI(无需重新编码)的方法,也适用于带有APP14标记的Adobe JPEG,这种类型并不少见。


0
尝试使用在线工具https://convert.town/image-dpi。对于我这个JFIF jpg只更改了DPI而没有重新采样图像。其他软件建议可以在这个Super User问题中找到,但对我来说,这是唯一一个不更改图像中任何其他东西就能起作用的。

然而,我只有一个JFIF jpg,而您似乎有一个来自其他评论的Adobe jpg,所以您的结果可能会有所不同。

我使用Windows十六进制编辑器HxD验证了DPI更改和非重新采样,只需查看xenoid链接的维基百科文章中详细说明的4个字节即可。一旦知道哪些字节发生变化,就可以跳过网站,直接使用十六进制编辑器设置X和Y DPI。


不错的工具,谢谢建议,但我需要一个Windows命令行工具,而不是在线工具。 - mmj
他们在https://convert.town/downloaddpi提供了一个付费的Windows版本。虽然它不是命令行,但考虑到他们希望您向他们索取Mac/Linux版本,我相信他们也会很乐意制作一个Windows命令行版本。如果您不想为此付费,唯一剩下的选择可能是编写自己的脚本。我的猜测是,假设您的JPG文件都来自同一台相机/软件,因此可以以相同的方式编辑它们的十六进制代码,那么您可以为您的情况编写一个非常简单的Python脚本。 - Micah Lindstrom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接