识别塔木德页面上的文本区域
Identify text areas on a Talmud page
我有一个像这样的犹太法典页面:
我想找到带有 opencv
的文本区域以获得这样的结果,即每个文本都将像这样独立:
在附图中,每个区域都用不同的颜色标记,文字都有编号,重要的是要区分属于每个文字的区域,并将其与属于另一个文字的区域区分开来,数字顺序无关紧要。
根据文字之间的白色条纹,用眼睛做真的很容易,但我尝试用 opencv
做,但我做不到。
在下面的代码中,我尝试捕获所有字母并将它们变成黑色矩形,
然后放大每个矩形使其与相邻的矩形相交,
所以整个文本区域将是黑色的,文本之间将有一个清晰的白色条纹。
不知如何着手,有没有好的办法。
public List<Rectangle> getRects(Mat grayImg)
{
BlobCounter blobCounter = new BlobCounter();
blobCounter.ObjectsOrder = ObjectsOrder.None;
blobCounter.ProcessImage(grayImg);
IEnumerable<Blob> blobs = blobCounter.GetObjectsInformation();
var blackBlobs = grayImg.Clone;
foreach (var b in blobs)
blackBlobs.Rectangle(b.Rectangle.ToCvRect, Scalar.Black, -1);
var widths = blobs.Select(X => X.Rectangle.Width).ToList;
widths.Sort();
var median = widths(widths.Count / (double)2);
Mat erodet = new Mat();
Cv2.Erode(grayImg, erodet, null, iterations: median);
using (Window win = new Window())
{
win.ShowImage(erodet);
win.WaitKey();
}
}
在此先致谢,如有任何帮助,我们将不胜感激。
补充说明:
正如您在上图中所见,文本区域不是矩形,
但是这些区域可以描述为一堆不同大小的矩形的集合,一个放在另一个上面。
请注意,当两个矩形属于同一文本时,不要将一个矩形排列在另一个矩形旁边,而是将一个矩形排列在另一个矩形上方。
我想要实现的是这些矩形的集合,并且知道每个矩形它属于哪个文本。
答案可以使用任何编程语言,尤其是 C++
Python
和 C#
据我所知,您首先尝试执行布局分析以提取感兴趣的区域。这是一个研究领域,不是很出名,但对这个问题有一些很好的结果。如果您不确定使用 opencv
来解决此问题,我建议您查看 Optical Layout Recognition (OLR)。 LAREX open-source 工具是一个很好的起点。
我相信这个任务主要可以使用 morphological operations 来完成。
更容易显示 matlab, but opencv 中的概念具有等效操作。
我们首先粗略估计页面不同部分之间的差距大小。查看您的示例,间隙约为页面高度的 1%。
img = im2single(rgb2gray(imread('https://i.stack.imgur.com/LoV5x.jpg'))); % read the image into 1ch gray scale image in range [0, 1]
gap = ceil(size(img,1) * 0.01); % gap estimation
首先,我们想使用图像 dilation 创建一个掩码,其中同一部分中的所有单词都相互连接:
d1 = imdilate(img < 0.5, ones(gap));
结果为:
(如果不是因为打印机在每个部分的底部添加下一页的烦人文字,我们就已经完成了...)
有一些dilation没有填补的大空隙,我们可以用floodfill
补全:
f = imfill(d1, 'holes');
现在我们有了文本区域的完整遮罩:
使用erosion在不同部分之间进行剪切:
e = imerode(f, ones(1, 5*gap)); % erosion only horizontally
得到正确的分区,虽然太稀疏了:
向后扩张
d2 = imdilate(e, ones(1, 5*gap));
给出这个二进制掩码:
您现在可以简单地查看此二进制掩码的 connected components:
我希望这对我来说算作“Daf Yomi”...
更新:
下一步 - 从线段到矩形多边形需要一些几何操作,我将在这里概述方法并将实现细节留给您。
最终,我们希望每个线段都有一个边界 polygon,基本多边形是线段的矩形边界框。您必须实现这个“多边形”class。这个 class 的一个关键方法是“多边形减法”——即 poly_result = poly_a - poly_b
创建一个新的多边形 poly_result
,它是 poly_a
减去 poly_a
和 poly_b
.
算法如下:
- 为每个片段计算它的边界框、边界框的面积和片段中的实际像素数。
将每个线段的多边形初始化为其边界框。
- 根据像素数与边界框面积之间的比率降序对片段进行排序。
- 每个段按降序排列:
从该段中减去所有先前的多边形。
你应该得到这样的东西:
对于第二张图片:
我有一个像这样的犹太法典页面:
opencv
的文本区域以获得这样的结果,即每个文本都将像这样独立:
在附图中,每个区域都用不同的颜色标记,文字都有编号,重要的是要区分属于每个文字的区域,并将其与属于另一个文字的区域区分开来,数字顺序无关紧要。
根据文字之间的白色条纹,用眼睛做真的很容易,但我尝试用 opencv
做,但我做不到。
在下面的代码中,我尝试捕获所有字母并将它们变成黑色矩形, 然后放大每个矩形使其与相邻的矩形相交, 所以整个文本区域将是黑色的,文本之间将有一个清晰的白色条纹。
不知如何着手,有没有好的办法。
public List<Rectangle> getRects(Mat grayImg)
{
BlobCounter blobCounter = new BlobCounter();
blobCounter.ObjectsOrder = ObjectsOrder.None;
blobCounter.ProcessImage(grayImg);
IEnumerable<Blob> blobs = blobCounter.GetObjectsInformation();
var blackBlobs = grayImg.Clone;
foreach (var b in blobs)
blackBlobs.Rectangle(b.Rectangle.ToCvRect, Scalar.Black, -1);
var widths = blobs.Select(X => X.Rectangle.Width).ToList;
widths.Sort();
var median = widths(widths.Count / (double)2);
Mat erodet = new Mat();
Cv2.Erode(grayImg, erodet, null, iterations: median);
using (Window win = new Window())
{
win.ShowImage(erodet);
win.WaitKey();
}
}
在此先致谢,如有任何帮助,我们将不胜感激。
补充说明:
正如您在上图中所见,文本区域不是矩形, 但是这些区域可以描述为一堆不同大小的矩形的集合,一个放在另一个上面。
请注意,当两个矩形属于同一文本时,不要将一个矩形排列在另一个矩形旁边,而是将一个矩形排列在另一个矩形上方。
我想要实现的是这些矩形的集合,并且知道每个矩形它属于哪个文本。
答案可以使用任何编程语言,尤其是 C++
Python
和 C#
据我所知,您首先尝试执行布局分析以提取感兴趣的区域。这是一个研究领域,不是很出名,但对这个问题有一些很好的结果。如果您不确定使用 opencv
来解决此问题,我建议您查看 Optical Layout Recognition (OLR)。 LAREX open-source 工具是一个很好的起点。
我相信这个任务主要可以使用 morphological operations 来完成。
更容易显示 matlab, but opencv 中的概念具有等效操作。
我们首先粗略估计页面不同部分之间的差距大小。查看您的示例,间隙约为页面高度的 1%。
img = im2single(rgb2gray(imread('https://i.stack.imgur.com/LoV5x.jpg'))); % read the image into 1ch gray scale image in range [0, 1]
gap = ceil(size(img,1) * 0.01); % gap estimation
首先,我们想使用图像 dilation 创建一个掩码,其中同一部分中的所有单词都相互连接:
d1 = imdilate(img < 0.5, ones(gap));
结果为:
(如果不是因为打印机在每个部分的底部添加下一页的烦人文字,我们就已经完成了...)
有一些dilation没有填补的大空隙,我们可以用floodfill
补全:
f = imfill(d1, 'holes');
现在我们有了文本区域的完整遮罩:
使用erosion在不同部分之间进行剪切:
e = imerode(f, ones(1, 5*gap)); % erosion only horizontally
得到正确的分区,虽然太稀疏了:
向后扩张
d2 = imdilate(e, ones(1, 5*gap));
给出这个二进制掩码:
您现在可以简单地查看此二进制掩码的 connected components:
我希望这对我来说算作“Daf Yomi”...
更新:
下一步 - 从线段到矩形多边形需要一些几何操作,我将在这里概述方法并将实现细节留给您。
最终,我们希望每个线段都有一个边界 polygon,基本多边形是线段的矩形边界框。您必须实现这个“多边形”class。这个 class 的一个关键方法是“多边形减法”——即 poly_result = poly_a - poly_b
创建一个新的多边形 poly_result
,它是 poly_a
减去 poly_a
和 poly_b
.
算法如下:
- 为每个片段计算它的边界框、边界框的面积和片段中的实际像素数。
将每个线段的多边形初始化为其边界框。 - 根据像素数与边界框面积之间的比率降序对片段进行排序。
- 每个段按降序排列:
从该段中减去所有先前的多边形。
你应该得到这样的东西:
对于第二张图片: