如何使用 Bash 脚本、shell 算法和 ImageMagick 在图像上绘制线条和文本
学习如何使用 Linux® 命令行和一些基本的 Bash 脚本编写技巧,以便使用 ImageMagick 在图像上绘制线条和文本,并同时创建一个像素标尺。
有时,我需要在一张图像或一块空白画布上绘制几根线条和一些文本。就在最近,我需要将一个像素标尺的一张简单图像包含在我的一篇 developerWorks 文章中。我希望这张图像宽 572 像素,以满足目前 developerWorks 文章图像的推荐标准,就像在 “developerWorks 投稿图片处理” 中描述的一样。Linux 和 Windows 有许多屏幕像素标尺可用(见 参考资料),但我只想要一个简单的 GIF、JPEG 或 PNG 图像。本文介绍如何使用 Bash 脚本、shell 算法和 ImageMagick 创建一个像素标尺。
制作一张画布
艺术家需要的首要装备就是一张画布,因此让我们使用 XC 假想类型在 ImageMagick 中创建一张画布。我们还需要一种颜色,可以是 ImageMagick 中的众多已命名颜色中的一种,也可以是一种自定义颜色。(从 参考资料 获取到 ImageMagick 文档的链接,这些文档包含一个假想类型和颜色名称的完整列表)。清单 1 展示如何使用 convert
命令制作一张浅蓝色的 572x100 画布。(convert
命令通常用于转换到一种不同的图像格式,或者以另一种方式改变一张图像)。
清单 1. 创建一张画布
convert -size 572x100 xc:lightblue ruler1.gif
图 1 展示了我们的新画布。
图 1. 用于标尺的天蓝色 572x100 像素画布
添加几根线条
现在我们有了一张画布,让我们绘制一些标记来显示沿着标尺的不同点。72 ppi(像素/每英寸)是基于 Web 的图形的标准,因此让我们将我们的第一个标记设置为 72 像素并使它从图像底部扩展 30 像素。另外,让我们用黑色填充这个线条,并为其指定两个端点。
清单 2. 将一根线条添加到这张画布
convert -fill black -draw "line 72,70 72,100" ruler1.gif ruler2.gif
图 2 展示了我们的新图像,图像上面绘制了一条线。
图 2. 带有一根垂直线的画布,线条距画布左边 72 像素
在这个标尺上绘制每个线条都重复上述操作,这很乏味,因此让我们使用 seq
命令来生成一个相距 72 像素的水平偏移的列表,同时使用一个 for
循环来在这个标尺上绘制主线条(如清单 3 所示)。
清单 3. 在画布上每隔 72 像素添加一条线
convert -fill black -draw "$(for n in $(seq 0 72 572) ;\
do echo line $n,70 $n,100 ; done)" ruler1.gif ruler3.gif
注意,我们使用了两个命令替代来为绘制操作生成单独的线条规范。我们还在 0 位置 —— 左侧边缘 —— 放置了一个标记。现在,图 3 看起来更像一个标尺了。
图 3. 从左边缘起每 72 像素一条垂直线的画布
对更多线条编写脚本
此时,我们的命令行变得有点复杂了,但是我们的标尺上仍然只有主要标记。现在是时候编写脚本了。在清单 4 中,我们使用 shell 算法和 for
循环在已经有的标记的中间放置一个 20 像素的标记,并在这些较长的标记之间每隔 6 像素放置一些 10 像素的标记。为完成本文的任务,我们将调用脚本 buildruler.sh
并将它放置在我们的工作目录中。
清单 4. 将一个完整的标记集添加到这个标尺
#!/bin/bash
# Take user parameters or set defaults
rulername="$1"
rulerlength="$2"
rulername="${rulername:=ruler.gif}"
rulerlength="${rulerlength:=572}"
drawstring=""
#Build the line definitions for the ruler marks
for x1 in `seq 0 72 $rulerlength`; do
drawstring="$drawstring line $x1,70 $x1,100"
for x2 in 0 36; do
(( offset = $x1 + $x2 ))
drawstring="$drawstring line $offset,80 $offset,100"
for x3 in `seq 6 6 30`; do
(( offset2 = $offset + $x3 ))
drawstring="$drawstring line $offset2,90 $offset2,100"
done
done
done
#Create the ruler
convert -size "${rulerlength}x100" xc:lightblue -fill black \
-draw "$drawstring" "$rulername"
注意,我们添加了两个参数,以便用户可以更改标尺的名称并指定其长度。现在,我们的标尺看起来就像图 4 一样。这个标尺是使用命令 ./buildruler.sh ruler4.gif
生成的。
图 4. 带有完整的标记集的标尺,标记之间相距 6 像素
添加文本
引用
脚本编写的一个要点是要知道何时需要引号,何时不需要,以及如何将引用的字符串作为参数传递。特别需要注意这些脚本中使用的引号。
现 在,让我们将标尺上的较大的标记的标签设置为数字 0、72、144...,依次类推。我们需要告知 ImageMagick 使用哪种字体,字体的大小和颜色是什么。显然,数字 0 应该在标尺的左边缘位置,但是如果我们将那些两位数和三位数的数字放置在那些 72 像素点,它们看起来会不平衡。为了更正这个问题,我们需要将它们向左侧稍微偏移一些,使它们正好位于标记的正上方。放大的脚本如清单 5 所示。
清单 5. 向标尺添加标签
#!/bin/bash
# Take user parameters or set defaults
rulername="$1"
rulerlength="$2"
rulername="${rulername:=ruler.gif}"
rulerlength="${rulerlength:=572}"
drawstring=""
#Build the line definitions for the ruler marks
for x1 in `seq 0 72 $rulerlength`; do
drawstring="$drawstring line $x1,70 $x1,100"
for x2 in 0 36; do
(( offset = $x1 + $x2 ))
drawstring="$drawstring line $offset,80 $offset,100"
for x3 in `seq 6 6 30`; do
(( offset2 = $offset + $x3 ))
drawstring="$drawstring line $offset2,90 $offset2,100"
done
done
done
#Add the labels
labelfont="-fill black -font helvetica -pointsize 24 -draw"
labelstring="text 0,60 \"0\" "
for x1 in 72; do
(( offset = $x1 - 12 ))
labelstring="$labelstring text $offset,60 \"$x1\" "
done
for x1 in `seq 144 72 $rulerlength`; do
(( offset = $x1 - 18 ))
labelstring="$labelstring text $offset,60 \"$x1\" "
done
#Create the ruler
convert -size "${rulerlength}x100" xc:lightblue -fill black \
-draw "$drawstring" $labelfont "$labelstring" "$rulername"
现在,我们的标尺如图 5 所示。
图 5. 带有完整标记集和标签的标尺
定位文本
那么,我们如何知道文本应该偏移多少,应该放置在距离顶部多远的位置?想当然的答案可能是:试验和猜测,但这不是一个好办法。
为了更好地放置文本,我们需要知道包含文本的方框有多大。但这取决于字体,字体是否成比例,以及其他一些不容易计算的因素。幸运的是,我们可以以同样的方法使用 ImageMagick 创建一个画布,我们还能创建一个 label。我们将创建一个标题 Pixel Ruler 并将它设置为 36 点的字体。这个标签作为一个图像创建,我们可以使用 identify
命令来确定其大小(如清单 6 所示)。
清单 6. 创建一个标题并测量其大小
$ convert -fill NavyBlue -background Lavender -font helvetica -pointsize 36 \
label:"Pixel Ruler" label.gif
$ identify "label.gif"
label.gif GIF 175x36 175x36+0+0 8-bit PseudoClass 256c 1.98kb
我将文本设置为海军蓝并添加一个淡紫色的背景颜色,以便这个图像从这个页面背景中凸显出来。但是,不管是否使用背景颜色,得到的图像的大小都相同。生成的标签图像如图 6 所示。
图 6. 标签图像
比编写一个文件甚至更好的是,我们可以使用 ImageMagick INFO 类确定关于文本的更多信息,比如基线的位置。我们将文本写到一张足够大的画布上,然后沿着文本的边缘修剪画布(如清单 7 所示)。
清单 7. 使用 INFO 图像类确定字体信息
$ convert -size 572x100 xc:lightblue -font helvetica -pointsize 36 \
-fill black -undercolor lavender -annotate +40+50 'Pixel Ruler' -trim info:
xc:lightblue XC 174x36 572x100+40+23 16-bit DirectClass
注解 的文本从一个点开始,该点的 y 坐标轴代表文本的基线。上面的输出显示,文本填充到一个大小为 174x36 像素的方框中。这个结果和此前的结果之间存在 1 个像素的差距,但这并不重要。方框的顶部距离原始画布的顶部 23 像素。由于基线位于原始画布顶端下方 50 像素处,这意味着基线实际上距离文本顶端 27 像素(50 减去 23),为下行线留出了 9 个像素。图 7 将一个图像放置在未修剪的画布上来说明这种关系。
图 7. 标签度量
现在我们已经知道了这个标签的尺寸,让我们将基线放置在距离图像顶部 40 像素的位置并使其水平居中。我们的最终脚本如清单 8 所示并可供 下载。
清单 8. 最终脚本
#!/bin/bash
# Take user parameters or set defaults
rulername="$1"
rulerlength="$2"
rulername="${rulername:=ruler.gif}"
rulerlength="${rulerlength:=572}"
drawstring=""
#Build the line definitions for the ruler marks
for x1 in `seq 0 72 $rulerlength`; do
drawstring="$drawstring line $x1,70 $x1,100"
for x2 in 0 36; do
(( offset = $x1 + $x2 ))
drawstring="$drawstring line $offset,80 $offset,100"
for x3 in `seq 6 6 30`; do
(( offset2 = $offset + $x3 ))
drawstring="$drawstring line $offset2,90 $offset2,100"
done
done
done
#Add the labels
labelfont="-fill black -font helvetica -pointsize 24 -draw"
labelstring="text 0,60 '0' "
for x3 in 72; do
offset3=$(($x3 - 12 ))
labelstring="$labelstring text $offset3,60 '$x3' "
done
for x4 in `seq 144 72 $rulerlength`; do
offset4=$(( $x4 - 18 ))
labelstring="$labelstring text $offset4,60 '$x4' "
done
#Add a title
titledimension=$(convert -size 572x100 xc:lightblue -font helvetica \
-pointsize 36 -fill black -undercolor lavender\
-annotate +40+50 'Pixel Ruler' -trim info: | awk ' {print $3 } ')
titlewidth=${titledimension%x*}
titlefont="-fill NavyBlue -font helvetica -pointsize 36"
titlepos=$(( (($rulerlength - $titlewidth)) / 2 ))
titletext="text $titlepos,30 'Pixel Ruler' "
#Create the ruler
convert -size "${rulerlength}x100" xc:lightblue \
-fill black -draw "$drawstring" $labelfont "$labelstring" \
$titlefont -draw "$titletext" "$rulername"
最终的标尺如图 8 所示。
图 8. 带有标题的最终标尺
对于那些没有艺术技巧的人来说,这算得上一种不错的方法。
一个实践示例
如果图像被缩进 — 比如在列表中 — developerWorks 上允许的最大宽度就相应减小。因此,您可以使用这些方便的标尺来检查随文章一起提交的图像。
- 这是在一个未排序的列表中的一个短标尺,它使用
./buildruler.sh ruler9.gif 400
命令创建。
图 9. 一个 400 像素标尺
- 这是一个进一步缩进的更短的标尺,它使用
./buildruler.sh ruler10.gif 300
命令创建。
图 10. 一个 300 像素标尺
结束语
通过本文的简单实践,您看到了使用 ImageMagick 通过脚本编写包含线条和文本的图像的一些基本技巧。您将在我们的文章 “通过命令行处理图形 -- 使用 ImageMagick 进行翻转、缩放大小、旋转以及更多操作” 和 “通过命令行进一步处理图形 -- 在 Linux 上使用 ImageMagick 的提示与技巧” 中发现更多技巧。您也可以在 ImageMagick 的主页上找到更多示例,从 参考资料 部分获取相关链接。
本 文中的脚本并不是无懈可击的。例如,对于长度是不是对一个有意义的标尺足够大的正数值,或者指定的文件是不是 ImageMagick 的有效图像文件类型,我们没有进行验证。您可能还会发现其他问题。例如,标尺标签可能像图 10 中那样被截断,这是我们的脚本没有考虑的一个问题。
您还可以任意添加多个参数,例如,尝试添加颜色或标尺高度参数。
尽管这个技巧主要关注在 Linux 上使用 ImageMagick,ImageMagick 也可用于 Windows 等其他平台上。尝试在您喜欢的平台上使用脚本工具实践这些技巧。
参考资料
学习
- “developerWorks 投稿图片处理”(developerWorks,2008 年 7 月)展示如何为 developerWorks 文章和教程制作和提交技术图形。
- “通过命令行处理图形 -- 使用 ImageMagick 进行翻转、缩放大小、旋转以及更多操作”(developerWorks,2003 年 7 月)和 “通过命令行进一步处理图形 -- 在 Linux 上使用 ImageMagick 的提示与技巧”(developerWorks,2004 年 3 月)展示从命令行使用 ImageMagick 的其他技巧。
- 在 ImageMagick Web 站点 查找手册页和 ImageMagick 用途的多个示例。
- 在 可爱的 Python:将 XML 和 Python 结合起来介绍 Python 的 XML 工具 可以找到更多针对 Linux 开发人员的参考资料。
- 在 developerWorks Linux 专区 寻找为 Linux 开发人员(包括 Linux 新手入门)准备的更多参考资料,查阅我们 最受欢迎的文章和教程。
- 在 developerWorks 上查阅所有 Linux 技巧 和 Linux 教程。
- 随时关注 developerWorks 技术活动和网络广播。
获得产品和技术
- 一些屏幕标尺可用于不同的操作系统:
- Gnome Screen Ruler (Linux Gnome) 可能包含在您的 Linux 发行版中。
- KRuler (Linux KDE) 是 KDE 图形包的一部分,可能包含在您的 Linux 发行版中。
- Free ruler (Mac OS X) 是针对 Mac 的一个免费屏幕标尺。
- JR Screen Ruler (Windows) 是针对 Windows 的一个免费虚拟标尺。
- 使用可以直接从 developerWorks 下载的 IBM 产品评估试用版软件,在 Linux 上构建您的下一个项目。
讨论
- 加入 My developerWorks 社区;您可以通过个人信息和定 制主页获得符合自己的兴趣的 developerWorks 文章,并 与其他 developerWorks 用户进行交流。