我有几千张图像,其主要特征如附图所示:所有图像都在(几乎)黑色的框架中,而实际图像内容几乎总是在白色背景上。
现在我想旋转实际图像内容,使该内容的左边框垂直。然后我想裁剪(修剪)图像,以便丢弃黑色边框,但完全保留实际内容。也就是说,即使包含实际内容的区域不是完美的矩形,也必须保留该区域的所有内容,这意味着之后黑色边框的剩余部分仍然可见。
考虑到我想要以这种方式处理的图像数量,我想我必须使用命令行工具来完成。过去,我曾使用 ImageMagick 完成此类工作(进行更简单的转换),但我真的不介意结合使用几种不同的工具来完成这项任务。
我已经尝试过的:
我研究过如何对图像进行倾斜校正,这种方法大多数时候都有效。但是,我发现的倾斜校正方法是将文本行设为水平。这很好,因为它使阅读更容易,但当然在大多数情况下,保存文本的区域的边框之后不会分别垂直或水平。这不是我想要的。
为了更详细地解释,我想:
- 检测黑框和包含实际图像内容的区域之间的左边缘。
- 沿着该边缘画一条(不可见的)直线。
- 确定该线与垂直线之间的角度。
- 将整个图像旋转该角度(负数),使得步骤 1 中的边缘变为垂直。
- 修剪图像,丢弃尽可能多的黑色框架,但保留包含实际内容的完整区域(因此,如果该区域不是完美的矩形,则接受保留黑色框架的其余部分)。
有人可以解释一下如何做到这一点吗,最好使用命令行工具?
对于简单干净的示例,您可以执行以下操作:使用 imagemagick
trim
删除大部分外边框,然后尝试旋转 1 度(为现在较大图像的新添加像素添加匹配的黑色背景),再次修剪,然后查看图像尺寸是否缩小。取最佳缩小尺寸。对于我得到的测试图像:
如果实际帧比示例的单色性稍差一些,您可以尝试
-trim
在选项前加上前缀,-fuzz 15%
以获得更近似的像素颜色比较。此答案试图通过使用 的功能来加快速度
-trim
,即在新图像的信息中保留旧画布内新图像的偏移量。首先,我们进行全局修剪以获得图像,其中:所需白色空间的最上角应与图像顶部相邻,所需白色空间的最左角应与图像左侧相邻。这是下图中虚线黄色矩形。我们重新分页此图像,使画布与新图像的大小相同。
顶部的薄水平切片(高度见
$stripwidth
下方脚本)被放入文件 中line1.png
,即红色矩形。它被修剪(放入文件 中out4.png
),如蓝色矩形所示。%X
结果图像的箭头偏移量应为找到最上角的位置。为了获得更好的估计,修剪图像的一半宽度(%w/2
)被添加到此(下图中夸大了)。类似地,切出一个薄的垂直切片并修剪。偏移量
%Y
是找到最左角的位置,加上一半的高度%h/2
。例如,如果红色条带的修剪结果
line1.png
为蓝色out4.png
,则identify out4.png
可能输出这表示图像为 14x2 像素,画布(
line1.png
)大小为 387x432,x 轴偏移量为 36 像素,y 轴偏移量为 0 像素。现在,我们得到了左上角三角形对边和邻边的长度。如果我们使用反正切计算角度,这就是使这个三角形消失所需的旋转,从而使上角与左边缘对齐。
请注意,
a()
中的 是 arctan(以弧度为单位)bc
。identify -format
用于%w %h %X %Y
获取原始画布中修剪图像的宽度、高度、x 偏移量、y 偏移量。 额外的0
是因为 imagemagick 添加了前导+
符号,并且bc
不处理它。结果是 5.76 度,如以下
sh -x
输出所示:以下解决方案基于@meuh 的想法。如果您觉得我的解决方案有用,请为他的答案点赞。首先,我将发布我的代码,然后解释我所做的更改,仅提及不明显的内容。
该代码基于一些对于我必须处理的图像来说是安全的假设:
这里的想法是,我们实际上不需要找到白色区域的角。这很重要,因为对我来说,可靠地找到角是不可能的,即使尝试不同的 值
stripwidth
。我的真实世界图像来自扫描仪,这意味着白色区域的边缘不是很清晰。这使得找到顶角变得相当困难。通常,我们可以按以下方式找到顶边:在第一个
convert
操作(修剪)之后,转到第一行像素(在顶部),从左到右沿着它走,并在第一个白色像素处停止;然后你就找到了角落。如果白色区域已经是水平的或接近水平的,这实际上是不可能的,并且顶部边缘越不“清晰”,则越困难。
因此,我将第二次操作裁剪的区域(“线”)
convert
从顶部边缘移开,距离为图像高度的十分之一。这确保了我们在该线(文件名)中有一个清晰且相对锐利的从黑色到白色的水平过渡line1.png
。我想避免在第一行出现两次水平过渡(例如左侧黑色 -> 白色,右侧白色 -> 黑色)。这只是因为我不知道
imagemagick
如果该行中有多个过渡,下一步会发生什么。因此,在裁剪该行时,我只使用一半的图像宽度。[旁注:该行中仍可能有多个过渡,具体取决于白色区域的内容(例如黑色文本),但这在我的第一次测试中没有造成问题。]第三个
convert
操作输出类似 的内容0x10+45+0
。部分0x10
表示上述转换的尺寸,+45
是X
转换开始的坐标。这是我们想要的值;构造read
将其保存在 中x1
。第二条“线”(文件名 )也采用同样的方法
line2.png
,从图像底部裁剪出十分之一的图像高度(第四个convert
操作)。在下一行(第五个convert
操作)中,X
将第二行中黑色到白色的过渡坐标保存在 中x2
。现在我们可以计算出第 1 行和第 2 行两个过渡之间的水平和垂直距离,并根据这些距离计算出旋转角度。
我使用 Perl 来执行该计算,因为
bc
它没有提供该atan2()
函数,在这种情况下我显然更喜欢它,因为它能够处理水平或垂直距离(甚至两者)的情况0
。其余部分应该是不言自明的。