拆分图像中的数字

时间:2018-05-14 10:08:44

标签: opencv tensorflow machine-learning computer-vision mnist

我已经获得了许多手工填写的报告。报告中的一列包含一个时间戳,我想在不经过手动报告的情况下尝试识别。

我正在玩分裂时代的想法,例如00:30,四位数,并通过MNIST培训的分类器运行这些数字,以识别实际的时间戳。

当我手动提取Photoshop中的四位数字并通过MNIST分类器运行时,它完美地运行。但到目前为止,我还没有能够弄清楚如何以编程方式将数字序列分成单个数字。我尝试在OpenCV中使用不同类型的countour查找,但它没有非常可靠地工作。

有什么建议吗?

我已经added a screenshot报告中的一些相关列。

2 个答案:

答案 0 :(得分:0)

将文本分解为单个字符并不像最初听起来那么容易。您可以尝试找到一些规则并通过它来操纵图像,但是会有太多例外。例如,你可以尝试找到不相交的标记,但是图像中的第四个标记 0715 有它" 5"分为三个部分,其中一个 17.00 有两个零重叠。

你对水平线非常幸运 - 至少很容易将不同的条目分开。但是你必须提出很多与半固定字符宽度相关的想法,一个" soft"不相交规则等。

两年前我做了一个类似的项目,最后我们使用了一个名为Tesseract的外部开源库。此处使用 罗马 数字识别this article,精确度达到约90%。您可能还想查看Lipi Toolkit,但我对此没有任何经验。

您可能还想考虑只训练网络一次识别四位数字。因此,输入将是具有四个手写数字的整个字段,输出将是四个数字。让网络整理出角色所在的位置。如果你有足够的训练数据,这可能是最简单的方法。

修改 受@ Link的回答启发,我想出了这个想法,你可以尝试一下。一旦您提取了两条线之间的区域,修剪图像以摆脱周围的空白区域。然后对有关人物的大小进行有根据的猜测。使用该区域的高度?然后在图像上创建一个滑动窗口,并一直运行识别。很可能会有四个峰值对应于四位数。

答案 1 :(得分:0)

我会做这样的事情(没有代码,只要它只是一个想法,你可以测试它,看看是否有效):

  1. 按照Rick M.上面的建议,为每组数字提取每个区域。因此,您将在图像形式下拥有许多Kl [hour]矩形。

  2. 对于每个这些矩形提取(使用OpenCV轮廓特征)每个ROI。如果您不需要,请删除Kl(您知道此投资回报率的维度(可以使用img.shape进行计算)并且它们的维度大致相同)

  3. 使用上面使用的相同脚本提取所有数字。您可以查看我的问题/答案,找到执行此操作的一些代码。 在某些情况下,您会遇到下划线问题。在SO上搜索这个,很少有完整的代码解决方案。

  4. 现在,关于分裂。我们知道ROI是小时格式,因此hh:mm(或4位数)。一个简单的(并且非常基本的)解决方案是将两个数字分开的ROI分成两半。这是一个原始的解决方案,但在你的情况下应该表现良好,因为附加的数字只是2。

  5. 有些数字会以“缺失的部分”输出。使用某些erosion/dilation/skeletonization可以避免这种情况。

  6. 在这里你没有字母,只有数字,所以MNIST应该运作良好(不完美,记住这一点)。

    在少数情况下,提取数据并不是一项艰巨的任务,但识别数字会让你有点汗流。

    我希望我能提供一些代码,以便尽快显示上述步骤。

    编辑 - 代码

    这是我制作的一些代码。最终输出是:

    output

    代码可以100%使用此图片,因此,如果某些内容不适合您,check folders/paths/modules installation

    希望这会有所帮助。

    import cv2
    import numpy as np
    
    # 1 - remove the vertical line on the left
    
    img = cv2.imread('image.jpg', 0)
    # gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(img, 100, 150, apertureSize=5)
    
    lines = cv2.HoughLines(edges, 1, np.pi / 50, 50)
    for rho, theta in lines[0]:
        a = np.cos(theta)
        b = np.sin(theta)
        x0 = a * rho
        y0 = b * rho
        x1 = int(x0 + 1000 * (-b))
        y1 = int(y0 + 1000 * (a))
        x2 = int(x0 - 1000 * (-b))
        y2 = int(y0 - 1000 * (a))
    
        cv2.line(img, (x1, y1), (x2, y2), (255, 255, 255), 10)
    
    cv2.imshow('marked', img)
    cv2.waitKey(0)
    cv2.imwrite('image.png', img)
    
    
    # 2 - remove horizontal lines
    
    img = cv2.imread("image.png")
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    img_orig = cv2.imread("image.png")
    
    img = cv2.bitwise_not(img)
    th2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 15, -2)
    cv2.imshow("th2", th2)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    horizontal = th2
    rows, cols = horizontal.shape
    
    # inverse the image, so that lines are black for masking
    horizontal_inv = cv2.bitwise_not(horizontal)
    # perform bitwise_and to mask the lines with provided mask
    masked_img = cv2.bitwise_and(img, img, mask=horizontal_inv)
    # reverse the image back to normal
    masked_img_inv = cv2.bitwise_not(masked_img)
    cv2.imshow("masked img", masked_img_inv)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    horizontalsize = int(cols / 30)
    horizontalStructure = cv2.getStructuringElement(cv2.MORPH_RECT, (horizontalsize, 1))
    horizontal = cv2.erode(horizontal, horizontalStructure, (-1, -1))
    horizontal = cv2.dilate(horizontal, horizontalStructure, (-1, -1))
    cv2.imshow("horizontal", horizontal)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    # step1
    edges = cv2.adaptiveThreshold(horizontal, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 3, -2)
    cv2.imshow("edges", edges)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    # step2
    kernel = np.ones((1, 2), dtype="uint8")
    dilated = cv2.dilate(edges, kernel)
    cv2.imshow("dilated", dilated)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    im2, ctrs, hier = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    # sort contours
    sorted_ctrs = sorted(ctrs, key=lambda ctr: cv2.boundingRect(ctr)[0])
    
    for i, ctr in enumerate(sorted_ctrs):
        # Get bounding box
        x, y, w, h = cv2.boundingRect(ctr)
    
        # Getting ROI
        roi = img[y:y + h, x:x + w]
    
        # show ROI
        rect = cv2.rectangle(img_orig, (x, y), (x + w, y + h), (255, 255, 255), -1)
    
    cv2.imshow('areas', rect)
    cv2.waitKey(0)
    
    cv2.imwrite('no_lines.png', rect)
    
    
    # 3 - detect and extract ROI's
    
    image = cv2.imread('no_lines.png')
    cv2.imshow('i', image)
    cv2.waitKey(0)
    
    # grayscale
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    cv2.imshow('gray', gray)
    cv2.waitKey(0)
    
    # binary
    ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)
    cv2.imshow('thresh', thresh)
    cv2.waitKey(0)
    
    # dilation
    kernel = np.ones((8, 45), np.uint8)  # values set for this image only - need to change for different images
    img_dilation = cv2.dilate(thresh, kernel, iterations=1)
    cv2.imshow('dilated', img_dilation)
    cv2.waitKey(0)
    
    # find contours
    im2, ctrs, hier = cv2.findContours(img_dilation.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    # sort contours
    sorted_ctrs = sorted(ctrs, key=lambda ctr: cv2.boundingRect(ctr)[0])
    
    for i, ctr in enumerate(sorted_ctrs):
        # Get bounding box
        x, y, w, h = cv2.boundingRect(ctr)
    
        # Getting ROI
        roi = image[y:y + h, x:x + w]
    
        # show ROI
        # cv2.imshow('segment no:'+str(i),roi)
        cv2.rectangle(image, (x, y), (x + w, y + h), (255, 255, 255), 1)
        # cv2.waitKey(0)
    
        # save only the ROI's which contain a valid information
        if h > 20 and w > 75:
            cv2.imwrite('roi\\{}.png'.format(i), roi)
    
    cv2.imshow('marked areas', image)
    cv2.waitKey(0)
    

    以下是以下步骤:

    1. 明白我写的东西;)。这是最重要的一步。

    2. 使用上面的代码片段(尤其是step 3),您可以删除提取的图片中剩余的Kl

    3. 为每个图像创建文件夹并提取数字。

    4. 使用MNIST识别每个数字。