一尘不染

在扫描的文档中分割文本行

python

我正在尝试寻找一种方法来打破已自适应阈值的扫描文档中的文本行。现在,我将文档的像素值存储为0到255之间的无符号整数,并获取每行像素的平均值,然后根据像素值的平均值是否为0将行划分为多个范围大于250,然后将其取为各行范围的中值。但是,此方法有时会失败,因为图像上可能会出现黑色斑点。

有没有更好的抗噪方法来执行此任务?

编辑:这是一些代码。“扭曲”是原始图像的名称,“剪切”是我要分割图像的位置。

warped = threshold_adaptive(warped, 250, offset = 10)
warped = warped.astype("uint8") * 255

# get areas where we can split image on whitespace to make OCR more accurate
color_level = np.array([np.sum(line) / len(line) for line in warped])
cuts = []
i = 0
while(i < len(color_level)):
    if color_level[i] > 250:
        begin = i
        while(color_level[i] > 250):
            i += 1
        cuts.append((i + begin)/2) # middle of the whitespace region
    else:
        i += 1

编辑2:添加了示例图像
在此处输入图片说明


阅读 250

收藏
2021-01-20

共1个答案

一尘不染

在输入图像中,您需要将文本设置为白色,将背景设置为黑色

在此处输入图片说明

然后,您需要计算账单的旋转角度。一种简单的方法是找到minAreaRect所有白点(findNonZero),然后得到:

在此处输入图片说明

然后,您可以旋转帐单,使文本为水平:

在此处输入图片说明

现在您可以计算水平投影(reduce)。您可以在每行中取平均值。th在直方图上应用阈值以解决图像中的一些噪点(在此我使用了0,即没有噪点)。仅背景的行将具有值>0,文本行在0直方图中将具有值。然后,获取直方图中白色bin的每个连续序列的平均bin坐标。那将是y您行的坐标:

在此处输入图片说明

这里的代码。它使用C ++,但是由于大多数工作都是使用OpenCV函数,因此应该可以轻松转换为Python。至少,您可以将其用作参考:

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

int main()
{
    // Read image
    Mat3b img = imread("path_to_image");

    // Binarize image. Text is white, background is black
    Mat1b bin;
    cvtColor(img, bin, COLOR_BGR2GRAY);
    bin = bin < 200;

    // Find all white pixels
    vector<Point> pts;
    findNonZero(bin, pts);

    // Get rotated rect of white pixels
    RotatedRect box = minAreaRect(pts);
    if (box.size.width > box.size.height)
    {
        swap(box.size.width, box.size.height);
        box.angle += 90.f;
    }

    Point2f vertices[4];
    box.points(vertices);

    for (int i = 0; i < 4; ++i)
    {
        line(img, vertices[i], vertices[(i + 1) % 4], Scalar(0, 255, 0));
    }

    // Rotate the image according to the found angle
    Mat1b rotated;
    Mat M = getRotationMatrix2D(box.center, box.angle, 1.0);
    warpAffine(bin, rotated, M, bin.size());

    // Compute horizontal projections
    Mat1f horProj;
    reduce(rotated, horProj, 1, CV_REDUCE_AVG);

    // Remove noise in histogram. White bins identify space lines, black bins identify text lines
    float th = 0;
    Mat1b hist = horProj <= th;

    // Get mean coordinate of white white pixels groups
    vector<int> ycoords;
    int y = 0;
    int count = 0;
    bool isSpace = false;
    for (int i = 0; i < rotated.rows; ++i)
    {
        if (!isSpace)
        {
            if (hist(i))
            {
                isSpace = true;
                count = 1;
                y = i;
            }
        }
        else
        {
            if (!hist(i))
            {
                isSpace = false;
                ycoords.push_back(y / count);
            }
            else
            {
                y += i;
                count++;
            }
        }
    }

    // Draw line as final result
    Mat3b result;
    cvtColor(rotated, result, COLOR_GRAY2BGR);
    for (int i = 0; i < ycoords.size(); ++i)
    {
        line(result, Point(0, ycoords[i]), Point(result.cols, ycoords[i]), Scalar(0, 255, 0));
    }

    return 0;
}
2021-01-20