文字中的“河流”检测


175

在TeX stackexchange上,我们一直在讨论如何在此问题的段落中检测“河流” 。

在这种情况下,河流是空白区域,是文本中单词间空格的意外对齐导致的。由于这可能会使读者分心,因此糟糕的河流被认为是不良印刷的征兆。带有河流的文本的一个示例是该河流,其中有两条河流沿对角线流动。

在此处输入图片说明

有兴趣自动检测这些河流,以便可以避免(可能是通过手动编辑文本)。Raphink在TeX级别上取得了一些进展(仅知道字形位置和边界框),但是我有信心,检测河流的最佳方法是进行某些图像处理(因为字形形状非常重要,TeX无法使用)。 。我尝试了多种方法从上述图像中提取河流,但是我提出的应用少量椭圆形模糊的简单想法似乎还不够好。我也试过Rad基于霍夫变换的过滤,但是我也没有。河流对于人眼/视网膜/大脑的特征检测电路非常明显,因此我认为可以将其转换为某种过滤操作,但我无法使其工作。有任何想法吗?

具体来说,我正在寻找一些操作来检测上图中的2条河流,但没有太多其他误报检测。

编辑: endolith问我为什么要采用基于图像处理的方法,因为在TeX中我们可以访问字形的位置,间距等,并且使用检查实际文本的算法可能更快,更可靠。我做事的原因是形状的字形可能会影响河流的明显程度,并且在文本级别上很难考虑这种形状(取决于字体,绑扎等)。有关字形形状如何重要的示例,请考虑以下两个示例,它们之间的区别在于,我用几乎相同宽度的其他字形替换了一些字形,以便进行基于文本的分析他们同样好/坏。但是请注意,第一个示例中的河流比第二个示例中的河流差很多。

在此处输入图片说明

在此处输入图片说明


5
+1我喜欢这个问题。我的第一个想法是霍夫变换,但可能需要进行一些预处理。也许首先是膨胀过滤器
datageist

我很惊讶Radon转换实际上没有用。你是怎么做到的?
endlith 2011年

@endolith:没什么复杂的。我ImageLines[]从Mathematica使用,带有或不带有一些预处理。我猜想这在技术上是使用霍夫变换而不是拉顿变换。如果适当的预处理(我没有尝试过datageist建议的膨胀过滤器)和/或参数设置可以使这项工作奏效,我不会感到惊讶。
Lev Bishop

Google Image Search for rivers也显示“蜿蜒”的河流。您想找到那些吗?cdn.ilovetypography.com/img/text-river1.gif
endolith

@endolith我想我最终想复制人类视觉系统的处理过程,该过程会使空间的某些配置分散注意力。由于这在蜿蜒的河流中也可能发生,所以我想抓住它们,尽管一般来说直河似乎更成问题。更好的方法是以一种与河流在阅读文字时能看到的强烈程度相对应的方式来量化河流的“坏处”。但这都是非常主观的,很难量化。首先,仅捕获几乎所有不好的河流而不会产生太多误报就可以了。
利夫·毕晓普

Answers:


135

我对此进行了更多考虑,并认为以下内容应该相当稳定。请注意,我仅限于进行形态学操作,因为这些应在任何标准图像处理库中可用。

(1)使用nPix x 1遮罩打开图像,其中nPix大约为字母之间的垂直距离

#% read image
img = rgb2gray('http://i.stack.imgur.com/4ShOW.png');

%# threshold and open with a rectangle
%# that is roughly letter sized
bwImg = img > 200; %# threshold of 200 is better than 128

opImg = imopen(bwImg,ones(13,1));

在此处输入图片说明

(2)使用1-by-mPix遮罩打开图像,以消除太窄而不能成为河流的任何东西。

opImg = imopen(opImg,ones(1,5));

在此处输入图片说明

(3)删除由于段落之间的空间或凹痕而引起的水平“河流和湖泊”。为此,我们删除所有正确的行,并使用已知不会影响我们先前发现的河流的nPix-by-1掩码打开。

要去除色淀,我们可以使用比nPix-by-nPix稍大的开口掩模。

在此步骤中,我们还可以丢弃所有太小而不能成为真实河流的东西,即,所有面积小于(nPix + 2)*(mPix + 2)* 4(这将给我们3条线)的区域。之所以加上+2,是因为我们知道所有对象的高度至少为nPix,宽度至少为mPix,并且我们想在其上略高一点。

%# horizontal river: just look for rows that are all true
opImg(all(opImg,2),:) = false;
%# open with line spacing (nPix)
opImg = imopen(opImg,ones(13,1));

%# remove lakes with nPix+2
opImg = opImg & ~imopen(opImg,ones(15,15)); 

%# remove small fry
opImg = bwareaopen(opImg,7*15*4);

在此处输入图片说明

(4)如果我们不仅对河流的长度感兴趣,而且对河流的宽度感兴趣,我们可以将距离变换与骨架相结合。

   dt = bwdist(~opImg);
   sk = bwmorph(opImg,'skel',inf);
   %# prune the skeleton a bit to remove branches
   sk = bwmorph(sk,'spur',7);

   riversWithWidth = dt.*sk;

在此处输入图片说明 (颜色与河流的宽度相对应(尽管颜色条偏离了2倍)

现在,您可以通过计算每个连接组件中像素的数量来获得河流的大致长度,并通过平均河流像素值来获得平均宽度。


这是应用于第二张“无河流”图像的完全相同的分析:

在此处输入图片说明


谢谢。我有Matlab,所以我将在其他一些文章上尝试一下,看看它会多么强大。
列夫·毕晓普

要将其重新集成到TeX中可能是另一个问题,除非我们可以将其移植到Lua。
phaphink 2011年

@LevBishop:我想我对这个问题了解得更好。新的解决方案应该相当健壮。
乔纳斯(Jonas)

@levBishop:还有一个更新。
乔纳斯(Jonas)

1
@LevBishop:刚刚注意到第二张图片。事实证明,基于形态的分析可以完成工作。
乔纳斯(Jonas)

56

在Mathematica中,使用腐蚀和霍夫变换:

(*Get Your Images*)
i = Import /@ {"http://i.stack.imgur.com/4ShOW.png", 
               "http://i.stack.imgur.com/5UQwb.png"};

(*Erode and binarize*)
i1 = Binarize /@ (Erosion[#, 2] & /@ i);

(*Hough transform*)
lines = ImageLines[#, .5, "Segmented" -> True] & /@ i1;

(*Ready, show them*)
Show[#[[1]],Graphics[{Thick,Orange, Line /@ #[[2]]}]] & /@ Transpose[{i, lines}]

在此处输入图片说明

编辑回答向导先生的评论

如果要摆脱水平线,请改为执行以下操作(可能有人可以简化它):

Show[#[[1]], Graphics[{Thick, Orange, Line /@ #[[2]]}]] & /@ 
 Transpose[{i, Select[Flatten[#, 1], Chop@Last@(Subtract @@ #) != 0 &] & /@ lines}]

在此处输入图片说明


1
为什么不摆脱所有水平线?(+1)
巫师先生

@先生。只是为了表明所有的线都被检测到了……
belisarius博士

1
但这不是问题的一部分,对吗?
Wizard先生

@先生。根据要求进行编辑
belisarius博士,

4
@belisarius在Hough变换中使用的坐标系在8.0.0之后更改为与Radon变换之一匹配。反过来,这改变了ImageLines的行为。总体而言,这是一种改进,尽管在这种情况下,您会更喜欢以前的行为。如果您不想尝试峰值检测,可以将输入图像的宽高比更改为接近1,并获得类似于8.0.0:的结果lines = ImageLines[ImageResize[#, {300, 300}], .6, "Segmented" -> True] & /@ i1;。综上所述,对于这个问题,形态学方法似乎更可靠。
Matthias Odisio

29

嗯...我想Radon变换并不是那么容易提取。(Radon变换基本上在“直视”边缘的同时旋转图像。这是CAT扫描的原理。)图像的变换会产生此正弦图,“河流”形成明亮的峰,并圈出:

在此处输入图片说明

旋转为70度时,可以清楚地看到此切片沿水平轴的左侧的峰值:

在此处输入图片说明

特别是如果文本首先是高斯模糊的:

在此处输入图片说明

但是我不确定如何从其余噪声中可靠地提取这些峰值。正弦图的明亮的顶端和底端代表了文本水平线之间的“河流”,您显然并不在乎。也许权重函数与角度的关系强调了更多的垂直线并最小化了水平线?

一个简单的余弦加权函数在此图像上效果很好:

在此处输入图片说明

找到90度的垂直河,这是正弦图中的全局最大值:

在此处输入图片说明

并在此图像上找到104度的图像,尽管首先模糊使它更准确:

在此处输入图片说明 在此处输入图片说明

(SciPy的radon()功能有点笨,或者我会将这个峰作为一条穿过河中的线映射回原始图像上。)

但是,经过模糊和加权后,它在图像的正弦图中找不到两个主要峰值之一:

在此处输入图片说明

它们在那里,但是它们被加权函数中间峰值附近的东西所淹没。有了正确的权重和调整这种方法可能工作,但我不知道正确的调整是什么。这也可能取决于页面扫描的属性。也许权重需要从切片中的总能量或诸如归一化之类的东西中得出。

from pylab import *
from scipy.misc import radon
import Image

filename = 'rivers.png'
I = asarray(Image.open(filename).convert('L').rotate(90))

# Do the radon transform and display the result
a = radon(I, theta = mgrid[0:180])

# Remove offset
a = a - min(a.flat)

# Weight it to emphasize vertical lines
b = arange(shape(a)[1]) #
d = (0.5-0.5*cos(b*pi/90))*a

figure()
imshow(d.T)
gray()
show()

# Find the global maximum, plot it, print it
peak_x, peak_y = unravel_index(argmax(d),shape(d))
plot(peak_x, peak_y,'ro')
print len(d)- peak_x, 'pixels', peak_y, 'degrees'

如果首先用不对称的高斯模糊了怎么办?即在水平方向上窄,在垂直方向上宽。
乔纳斯(Jonas)

@乔纳斯:这可能会有所帮助。主要问题是当背景随旋转变化很大时会自动从背景中挑选峰。不对称的模糊可以使水平条纹逐行平滑。
endlith 2011年

这至少可以很好地用于检测文本中线的旋转:gist.github.com/endolith/334196bac1cac45a4893
endolith 2014年

16

我使用了不同比例的派生特征(最高2阶)对像素进行了判别式分类器训练。

我的标签:

贴标

对训练图像的预测:

在此处输入图片说明

对其他两个图像的预测:

在此处输入图片说明

在此处输入图片说明

我认为这看起来很有希望,并且在获得更多训练数据和更智能功能的情况下可能会产生可用的结果。另一方面,我只花了几分钟就得到了这些结果。您可以使用开源软件ilastik自己重现结果。[免责声明:我是主要开发人员之一。]


2

(很抱歉,此帖子未附带出色的演示。)

如果您想使用TeX已经拥有的信息(字母和位置),可以手动将字母和字母对分类为在一个方向或另一个方向上“倾斜”。例如,“ w”具有SW和SE角坡度,“ al”组合具有NW角坡度,“ k”具有NE角坡度。(别忘了标点符号-引号后跟一个字母,该字母填充字形框的下半部分,建立了一个不错的斜率;引号后跟q特别强。)

然后,寻找在空间相对两侧的相应坡度的出现-SW到NE的河流为“ w al”,NW到SE的河流为“ k T”。当您在一行上找到一个时,请查看在上方/下方的行上是否发生了类似的事件(适当地向左或向右移动);当您发现其中的一部分时,可能会有一条河。

同样,显然,对于平原的垂直河流,只需寻找几乎垂直堆叠的空间即可。

通过测量坡度的“强度”,您可以变得更加复杂:由于坡度,前进框的多少是“空的”,从而对河流的宽度有所贡献。“ w”相当小,因为它的前进箱只有一个小角可用于河道,但“ V”非常强。“ b”比“ k”略强;柔和的曲线使河流边缘更具视觉上的连续性,使其更坚固且视觉上更宽。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.