2017-02-11 71 views
2

我已经对这个主题做了一些研究,但我认为我的问题与之前提出的问题有很大不同。简单的方法来检测和裁剪文本块(段落)的图像?

我的博士论文涉及OCR旧的字典并将结果自动转换为类似XML的数据库。这部分我已经想通了。但是,我想通过显示用于每个条目/词目的扫描片段来丰富最终结果。由于字典的长度将近9000页,因此手动完成是不可能的。

这是一个随机的页面的外观:http://i.imgur.com/X2mPZr0.png

由于每个条目总是等于一个段落,我想找到一种方法,每一个图像分割成与文本矩形(无OCR需要)作为单独的文件,像这个(没有绘制矩形): http://i.imgur.com/CWtQD6Q.png

好的是,我有的扫描是相同的形状和大小,并在边缘/文本对齐方面相似。每个段落也总是有一个标识。

糟糕的是,我大多是语言学家,而不是程序员。我的大部分经验是使用Ruby,XML和CSS。而且有些段落只有一行。

我所知道的一些方法做了类似的事情:

,但他们需要时间显著量我学习(特别是我在Pytho中有0知识n),我不知道他们是否允许不仅用于文本检测,还包括段落检测。

任何有关此事的意见或建议将不胜感激,尤其对新手友好。

回答

2

我有一些想法,分享......我想我会沿着这些路线进行:

第1步 - 阈值,以黑色和白色

我想我会用的OpenCV的Otsu分割为这个。

第2步 - 查找垂直的黑线

我将平均像素图像的每一行,并找到一个最低的平均水平,这应该是垂直线向上中间。下面输出代码:

Centreline at column: 1635 

步骤3 - 分割图像中的两个并修剪多余的空白空间

enter image description hereenter image description here

步骤4 - 箱式滤波器

我将箱式滤波器一个55x45的盒子与每个段落开头的缩进相匹配,然后是阈值,所以所有的段落开始都用黑色标出框。

enter image description here

我非常新的OpenCV的,但如下已编码的上述思路 - 我敢肯定它的地段可以作出更稳健和更有效,从而把它当作概念;-)

#include <iostream> 
#include <opencv2/opencv.hpp> 

using namespace cv; 
using namespace std; 

int 
main(int argc,char*argv[]) 
{ 
    // Load image 
    Mat orig=imread("page.png",IMREAD_COLOR); 

    vector<int> PNGwriteOptions; 
    PNGwriteOptions.push_back(CV_IMWRITE_PNG_COMPRESSION); 
    PNGwriteOptions.push_back(9); 

    // Get greyscale and Otsu-thresholded version 
    Mat bw,grey; 
    cvtColor(orig,grey,CV_RGB2GRAY); 
    threshold(grey,bw,0,255,CV_THRESH_BINARY|CV_THRESH_OTSU); 

    // Find vertical centreline by looking for lowest column average - i.e. darkest vertical bar 
    Mat colsums; 
    reduce(bw,colsums,0,CV_REDUCE_AVG); 
    double min,max; 
    Point min_loc, max_loc; 
    minMaxLoc(colsums,&min,&max,&min_loc,&max_loc); 
    cout << "Centreline at column: " << min_loc.x << endl; 

    namedWindow("test",CV_WINDOW_AUTOSIZE); 

    // Split image into left and right 
    Rect leftROI(0,0,min_loc.x,bw.rows); 
    Mat leftbw=bw(leftROI); 
    Rect rightROI(min_loc.x+8,0,bw.cols-min_loc.x-8,bw.rows); 
    Mat rightbw=bw(rightROI); 
    imshow("test",leftbw); 
    waitKey(0); 
    imshow("test",rightbw); 
    waitKey(0); 

    // Trim surrounding whitespace off 
    Mat Points; 
    Mat inverted = cv::Scalar::all(255) - leftbw; 
    findNonZero(inverted,Points); 
    Rect bRect=boundingRect(Points); 
    Mat lefttrimmed=leftbw(bRect); 

    inverted = cv::Scalar::all(255) - rightbw; 
    findNonZero(inverted,Points); 
    bRect=boundingRect(Points); 
    Mat righttrimmed=rightbw(bRect); 

    imwrite("lefttrimmed.png",lefttrimmed,PNGwriteOptions); 
    imwrite("righttrimmed.png",righttrimmed,PNGwriteOptions); 

    // Box filter with 55x45 rectangle to match size of paragraph indent on left 
    Mat lBoxFilt,rBoxFilt; 
    boxFilter(lefttrimmed,lBoxFilt,-1,Size(55,45)); 
    normalize(lBoxFilt,lBoxFilt,0,255,NORM_MINMAX,CV_8UC1); 
    threshold(lBoxFilt,lBoxFilt,254,255,THRESH_BINARY_INV); 
    imwrite("leftBoxed.png",lBoxFilt,PNGwriteOptions); 

} 

enter image description here

以防万一你需要一只手来建立这个代码 - 因为它似乎不平凡的编译和反对任何联系起来 - 我做了我CMakeLists.txt文件中像这样并将其存储在同一d作为源文件的irectory。然后,我创建了一个名为build做一个“乱源”建立一个子目录和构建过程是:

cd build 
cmake .. 
make -j 8 
./demo 

的CMakeLists.txt

cmake_minimum_required(VERSION 2.8) 
project(demo) 
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 
find_package(OpenCV) 
add_executable(demo main.cpp) 
target_link_libraries(demo ${OpenCV_LIBS}) 
+0

@Miki * *不是** ImageMagick ;-) –

+0

哇,谢谢! :)我仍然在研究OpenCV的基础知识,但这似乎是要走的路,并且将尽快进行测试。我也想到矩形的左上角作为坐标。我想最后一部分应该是找到一种方法将它们修剪成实际的矩形,并用它们来设置分割线。 – MrVocabulary

+0

你在Visual Studio/C++中做了这个吗? – MrVocabulary