使用标准 macOS 工具更改图像 DPI,无需重新编码 [关闭]

问题描述 投票:0回答:1

我有视网膜图像(144 DPI),必须使用TinyPNG进行压缩。不幸的是,该压缩将 DPI 设置为 72。我的目标是在压缩后将 DPI 恢复到 144。

一种选择是使用

sips
:

/usr/bin/sips --setProperty dpiWidth 144.0 --setProperty dpiHeight 144.0 IMAGE_PATH

但这会重新编码图像,这违背了目的,因为它失去了压缩带来的一些优化。更好的选择是使用

exiftool
:

exiftool -overwrite_original -XResolution=144 -YResolution=144 IMAGE_PATH

效果非常好,因为文件大小几乎保持不变。问题是它不是标准的 macOS 工具,因此可能不存在于用户的计算机中。

所以我正在寻找一种两全其美的解决方案。使用 Swift 是一种有效的方法,但我没有找到以这种方式完成任务的方法。

macos dpi image-compression
1个回答
0
投票

更新答案

macOS 附带了 Perl,因此这里有一种方法可以使用 Perl 完成您想要的操作。我确信任何有能力的 Swift 程序员都可以移植这个标准 Perl

我们想要做的是找到 144 dpi 在 PNG 文件中的外观,然后将该信息复制到现有的 PNG 文件中。它位于

pHYs
块中,来自 pNG 规范如下:

4 bytes of length (9 bytes in this case)
pHYs - the 4 ASCII characters that name the chunk
4 unsigned bytes giving the X resolution
4 unsigned bytes giving the Y resolution
1 byte specifying the units, where 0=undefined, 1=metres

您可以使用 ImageMagick 而不使用

dpi
创建 PNG,如下所示:

magick -size 256x256 gradient:red-blue image.png

还有一个 144 dpi 的,如下所示:

magick -size 256x256 -density 200 -units PixelsPerInch -density 144  gradient:red-blue image.png

然后你可以用

xxd
查看它们并找到
pHYs
块,你会看到它看起来像这样:

00 00 00 09 70 48 59 73 00 00 16 25 00 00 16 25 01 49 52 24 f0
<- len=9 -> <p H  Y s > < X res   > < Y res   > <1>< CRC  >

这里有一些(生锈且笨拙但有效)Perl,它采用该

pHYs
块并将其插入到第一个
IDAT
之前的 PNG 中:

#!/usr/bin/perl

use strict;
use warnings;

# Slurp entire image into variable "$content"
open my $in, '<:raw', 'image.png' or die "Can't open file $!";
my $content = do { local $/; <$in> };

# Create a new pHYs chunk and insert it before first IDAT chunk
$content =~ s/(....IDAT)/\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x16\x25\x00\x00\x16\x25\x01\x49\x52\x24\xf0$1/s;

# Save modified file
my $filename = 'result.png';
open(my $out, '>:raw', $filename) or die "Could not open file '$filename' $!";
print $out $content;
close $out;

原答案

正如我在评论中所说... PNG 文件只是一堆(很大程度上)独立的、独立的块。如果你会编写 Swift,你肯定可以编写一些代码来定位和重写

pHYs
块,它给出了图像的物理尺寸,即 dpi。示例这里 具体看“方法3”。

如果您始终想要固定的 144 dpi,您可以使用 Perl/Python 或您最喜欢的工具对文件进行硬编码甚至修补。它只有 9 个字节,加上标头和常量/不变校验和。

实际上,我认为 macOS 附带了

xxd
,因此只需将您的 TinyPNG 转换为纯十六进制流,即可替换
pHYs
并转换回二进制:

xxd -p TinyPNG |  sed 's/xxxpHYsxxx/yyypHYsyyy/' | xxd -p -r 144dpi.png

让我们制作一个 dpi=72 的 1x1 黑色 PNG 和另一个 dpi=144 的 PNG:

magick xc: 72dpi.png
exiftool -PixelsPerUnitY=72 -PixelsPerUnitX=72 72dpi.png
cp 72dpi.png 144dpi.png
exiftool -PixelsPerUnitY=144 -PixelsPerUnitX=144 144dpi.png

现在,如果我们对它们进行十六进制转储,我们可以看到

pHYs
的 dpi 位于倒数第四行 -
48
十六进制的两个实例是 X 和 Y 方向上的 72dpi:

xxd 72dpi.png
00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452  .PNG........IHDR
00000010: 0000 0001 0000 0001 0100 0000 0037 6ef9  .............7n.
00000020: 2400 0000 2063 4852 4d00 007a 2600 0080  $... cHRM..z&...
...
...
000000f0: 3954 3133 3a33 323a 3031 2b30 303a 3030  9T13:32:01+00:00
00000100: 6d36 df3d 0000 0009 7048 5973 0000 0048  m6.=....pHYs...H
00000110: 0000 0048 0131 ce5b a800 0000 0a49 4441  ...H.1.[.....IDA
00000120: 5408 d763 6800 0000 8200 81dd 436a f400  T..ch.......Cj..
00000130: 0000 0049 454e 44ae 4260 82              ...IEND.B`.

并且,十六进制的两个

90
是 144dpi:

xxd 144pdi.png
00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452  .PNG........IHDR
00000010: 0000 0001 0000 0001 0100 0000 0037 6ef9  .............7n.
00000020: 2400 0000 2063 4852 4d00 007a 2600 0080  $... cHRM..z&...
00000030: 8400 00fa 0000 0080 e800 0075 3000 00ea  ...........u0...
...
00000150: 0001 0000 0090 0000 0001 25af d629 0000  ..........%..)..
00000160: 0009 7048 5973 0000 0090 0000 0090 0186  ..pHYs..........
00000170: 428a fd00 0000 0a49 4441 5408 d763 6800  B......IDAT..ch.
00000180: 0000 8200 81dd 436a f400 0000 0049 454e  ......Cj.....IEN
00000190: 44ae 4260 82                             D.B`.

因此,将原始图像转换为十六进制流并使用

sed
进行修补,然后重新二进制化,我们得到:

xxd -p 72dpi.png | sed 's/7048597300000048000000480131ce5ba8/7048597300000090000000900186428afd/' | xxd -p -r > result.png
© www.soinside.com 2019 - 2024. All rights reserved.