给图片添加水印——来自命令行

作者: Dave Taylor

我们这些技术人员大多认为命令行是文本处理的最佳场所。使用 catgrep 和 shell 脚本进行文本处理是很自然的。但是,尽管您不一定能在典型的终端窗口中查看结果,但事实证明,从 shell 脚本中分析和处理图像非常容易。

在我的上一篇文章中,我介绍了出色的开源 ImageMagick 套件,它提供的功能和特性之多,简直可以用摇晃一棵树的所有树枝来形容。至于你为什么要对着一个软件摇树,我也不明白,但是,嘿,我只是在写这些东西,并没有思考我所说的话。

嗯...等一下。

无论如何,ImageMagick 包含各种程序,可让您以非常多的不同方式分析、处理甚至转换图像文件。

我的上一篇文章描述了如何进行设置以及一些简单的确认套件在您的计算机上正常工作的方法。现在,让我们从一个简单的任务开始,这个任务对于 Web 服务器可能很有用:一个检查图像文件并标记任何大于特定大小的文件的脚本。

实际上,让我们使用 8MB,因为这是 Facebook Open Graph 允许的最大尺寸,许多网站管理员都已经很清楚这个事实。

查找那些大的该死的图像文件

可以使用简单的 find 命令来识别大文件,但这里的目标是做一些更复杂的事情,所以让我们将其与 ImageMagick 命令 identify 结合使用。

这是一个循环,用于识别大于指定大小的文件


for name in `find *{png,jpg} -size +8M -print`
do
    echo file $name is bigger than 8MB
done

这是一个好的开始,但对于那些匹配的图像文件,更详细的信息将很有帮助,我不是在说 ls -l 输出!相反,让我们用更高级的东西替换基本的 echo 语句


dimensions=$(identify $name | cut -d\  -f3)
size=$(identify $name | cut -d\  -f7)
echo "File $name ($dimensions) has file size $size"

现在让我们进行一个快速测试


$ sh bigimages.sh
File screenshot-6.png (1800x490) has file size 9.639MB
$

它绝对比仅仅执行 ls -l 更具信息量,尤其是当您将其放入为托管客户端提供的自动电子邮件报告中时!

为什么?因为很多不精通技术的人直接从数码相机上传图像——这些图像可能非常大。他们在博客文章中调整了显示图像的大小,但原始文件却比需要的尺寸大得多,不必要地降低了网站的速度。

在继续之前,让我做一些评论。首先,请注意使用 find 进行的大小测试:-size +8Mfind 对数字和比较有一种奇怪的看法,测试 -size 8M 将仅匹配大小正好为 8MB 的文件——这没有用。如果没有 “M” 后缀,则测试将与 512 字节的块进行比较,因此 +8 将匹配很多文件(因为八个 512 字节的块仅为 4K,这对于图像文件来说太小了)。find 也知道 “K” 代表千字节,“G” 代表千兆字节,“T” 代表兆兆字节,是的,“P” 代表拍字节。

第二个需要注意的观察结果是在 echo 语句中使用引号。试试看。如果没有引号,shell 会抱怨使用括号,而使用单引号,它会显示变量名,而不是展开它们。这是一个很好的现实世界的提醒,shell 中存在细微但重要的引号细微差别!

添加水印

ImageMagick 最流行的用途之一不是识别图像尺寸,而是添加水印。您已经在网络上的图像上看到了水印。有时它是一个小的版权声明、一个 URL,甚至是标识图像来源或所有权的一个微小的图形错误。

效果不错,但是您如何做到呢?使用 convert 命令。实际上,如果您只想在图像底部添加文本叠加层,则命令很简单


convert $source label:'LinuxJournal.com'-append $output

但是,默认效果不是很吸引人,因此您不可避免地会想要对其进行一些自定义。要使字体更大一些,请使用 -pointsize,要将水印文本移动到右下角而不是其默认位置,请使用 -gravity

这稍微复杂一些,并展示了 ImageMagick 的一些怪异之处


convert $source -pointsize 80 -gravity east \
    label:'LinuxJournal.com' -append $output

当然,这可以很容易地放入脚本中,您可以将输出图像放在不同的目录中,也可以适当地重写源文件名。我最喜欢的实现后一种方法是这样的


predot=$(echo $name | rev | cut -d. -f2- | rev)
postdot=$(echo $name | rev | cut -d. -f1 | rev)
newname=$(echo ${predot}-wm.$postdot)

由于 cut 中没有 “last field” 选项,因此即使基本文件名包含点,抓取文件名后缀的方法是反转名称,抓取第一个字段,然后再反转一次。这样,您可以获取像 “red.rose.png” 这样的文件名,并将其重写为 “red.rose-wm.png” 以表示已添加水印的版本。

但是,如果您有一个小的图形错误,您想将其叠加在图像的左下角呢?

假设水印图形是用变量 $copy 指定的。考虑到这一点,可以使用 100% 的溶解来实现两个图像的良好融合


composite -dissolve 100 $copy $source $output

这会将图形放在结果图像的左上角,位于底层照片或图形的 “上方” 。

您可以使用 -gravity 将其移动到右下角——字面意思是这样。想象一下叠加在图像上的指南针:“north” 是上方,“east” 是右侧,“west” 是左侧,依此类推。使用前面显示的 convert -label 命令,默认位置是结果图像的底部,因此 “east” 的重力将标签移动到右下角。

但是,使用 composite,有更大的灵活性,因此将版权错误放置在右下角需要使用 “southeast” 的重力——像这样


composite -dissolve 100 -gravity southeast $copy $source $output

将其包装在一个 for 循环中,您就得到了一个方便的脚本,该脚本可以在底部添加文本水印,或者叠加图形水印或版权。

在两种情况下,请注意 ImageMagick 命令始终会创建一个新的输出文件。如果您想用包含水印的版本替换图像,则需要进行一些文件洗牌


composite -dissolve 100 $copy $source $output
mv $output $source

理想情况下,您应该检查以确保 composite 命令正常工作(而不是可能删除文件或产生神秘的错误)。想到了两种方法:检查 composite 命令的错误状态(序列 $?)或仅测试 $output 的存在。这是后者,因为这样您就不依赖于 composite 具有准确的返回代码


composite -dissolve 100 $copy $source $output
if [ -x $output ] ; then
  mv $output $source
else
  echo "Couldn't replace $source, $output wasn't created?"
fi

这样好吗?可能不是。-x 测试检查文件是否存在,但更好的做法是确保文件存在且大小不为零。那是 -s 标志。这是一个简单的开关,您就有了编写一个好的脚本的基础。

图像的魔力

ImageMagick 套件包含许多命令,这些命令具有数十到数百个参数。它们可能非常古怪,正如 gravity 设置所显示的那样——庆幸我没有深入研究 geometry 参数——但幸运的是,有很多在线教程和帮助页面。

请记住,一切都始于 https://imagemagick.org.cn

Dave Taylor 长期以来一直在 UNIX 和 Linux 系统上编写 shell 脚本。他是 Learning Unix for Mac OS XWicked Cool Shell Scripts 的作者。您可以在 Twitter 上找到他,账号为 @DaveTaylor,您也可以通过他的技术问答网站联系他:Ask Dave Taylor

加载 Disqus 评论