从OCR的图像中隔离单个数字

Sid*_*hay 5 ocr image-processing image-segmentation

执行单个数字字符识别的方法非常简单.但这是图像只包含一位数.

当图像包含多个数字时,我们不能使用相同的算法,因为整个位图是不同的.我们如何处理图像分割它,所以我们可以"modularise"的OCR操作在每个单独的数字?

gro*_*mal 1

但你想要执行的是图像分割问题,而不是数字分类问题。正如@VitaliPro 所说。两者都是 OCR 问题,但是(在极大的简化中)第一个问题是“这是什么字符”,第二个问题是“我这里有多少个字符”。您已经知道如何解决第一个问题了,让我们看看第二个问题通常是如何解决的。

您想要将图像分割成字符(分割中称为“区域”),然后将数字分类应用于每个区域。一种方法是执行分水岭分割,它使用颜色渐变来区分边缘和区域。

可以使用 Python 的 numpy/scipy/skimage 完成一个简单的分水岭,例如:

#!/usr/bin/env python

from PIL import Image
import numpy as np
from scipy import ndimage
from skimage import morphology as morph
from skimage.filter import rank

def big_regions(lb, tot):
    l = []
    for i in range(1, tot+1):
        l.append(((i == lb).sum(), i))
    l.sort()
    l.reverse()
    return l

def segment(img, outimg):
    img = np.array(Image.open(img))
    den = rank.median(img, morph.disk(3))
    # continuous regions (low gradient)
    markers  = rank.gradient(den, morph.disk(5)) < 10
    mrk, tot = ndimage.label(markers)
    grad     = rank.gradient(den, morph.disk(2))
    labels   = morph.watershed(grad, mrk)
    print 'Total regions:', tot
    regs = big_regions(labels, tot)
Run Code Online (Sandbox Code Playgroud)

morph在那里,我使用模块中的分水岭分割skimage

大多数情况下,对于分水岭,您应该将该区域放置在图像顶部以获得该区域的实际内容,而我在上面的代码中没有这样做。然而,数字或大多数文本不需要这样做,因为它应该是黑白的。

Watershed 使用颜色渐变来识别边缘,但也可以使用 Canny 或 Sobel 过滤器等过滤器。请注意,我正在对图像进行去噪(轻微模糊),以防止发现非常小的区域,因为这些区域很可能是伪影或噪声。使用 Canny 或 Sobel 滤波器可能需要更多的去噪步骤,因为滤波器会产生清晰的边缘。

分割的用途远不止字符分割,它通常用于图像上以区分重要区域(即外观非常相似的大区域)。例如,如果我matplotlib在上面添加一些内容并更改段函数,请说:

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.cm as cm

def plot_seg(spr, spc, sps, img, cmap, alpha, xlabel):
    plt.subplot(spr, spc, sps)
    plt.imshow(img, cmap=cmap, interpolation='nearest', alpha=alpha)
    plt.yticks([])
    plt.xticks([])
    plt.xlabel(xlabel)

def plot_mask(spr, spc, sps, reg, lb, regs, cmap, xlabel):
    masked = np.ma.masked_array(lb, ~(lb == regs[reg][1]))
    plot_seg(spr, spc, sps, masked, cmap, 1, xlabel)

def plot_crop(spr, spc, sps, reg, img, lb, regs, cmap):
    masked = np.ma.masked_array(img, ~(lb == regs[reg][1]))
    crop   = masked[~np.all(masked == 0, axis=1), :]
    crop   = crop[:, ~np.all(crop == 0, axis=0)]
    plot_seg(spr, spc, sps, crop, cmap, 1, '%i px' % regs[reg][0])

def segment(img, outimg):
    img = np.array(Image.open(img))
    den = rank.median(img, morph.disk(3))
    # continuous regions (low gradient)
    markers  = rank.gradient(den, morph.disk(5)) < 10
    mrk, tot = ndimage.label(markers)
    grad     = rank.gradient(den, morph.disk(2))
    labels   = morph.watershed(grad, mrk)
    print 'Total regions:', tot
    regs = big_regions(labels, tot)

    spr = 3
    spc = 6
    plot_seg(spr, spc, 1, img,    cm.gray,     1,   'image')
    plot_seg(spr, spc, 2, den,    cm.gray,     1,   'denoised')
    plot_seg(spr, spc, 3, grad,   cm.spectral, 1,   'gradient')
    plot_seg(spr, spc, 4, mrk,    cm.spectral, 1,   'markers')
    plot_seg(spr, spc, 5, labels, cm.spectral, 1,   'regions\n%i' % tot)
    plot_seg(spr, spc, 6, img,    cm.gray,     1,   'composite')
    plot_seg(spr, spc, 6, labels, cm.spectral, 0.7, 'composite')

    plot_mask(spr, spc, 7,  0, labels, regs, cm.spectral, 'main region')
    plot_mask(spr, spc, 8,  1, labels, regs, cm.spectral, '2nd region')
    plot_mask(spr, spc, 9,  2, labels, regs, cm.spectral, '3rd region')
    plot_mask(spr, spc, 10, 3, labels, regs, cm.spectral, '4th region')
    plot_mask(spr, spc, 11, 4, labels, regs, cm.spectral, '5th region')
    plot_mask(spr, spc, 12, 5, labels, regs, cm.spectral, '6th region')

    plot_crop(spr, spc, 13, 0, img, labels, regs, cm.gray)
    plot_crop(spr, spc, 14, 1, img, labels, regs, cm.gray)
    plot_crop(spr, spc, 15, 2, img, labels, regs, cm.gray)
    plot_crop(spr, spc, 16, 3, img, labels, regs, cm.gray)
    plot_crop(spr, spc, 17, 4, img, labels, regs, cm.gray)
    plot_crop(spr, spc, 18, 5, img, labels, regs, cm.gray)

    plt.show()
Run Code Online (Sandbox Code Playgroud)

(此示例不会自行运行,您需要将上面的其他代码示例添加到其顶部。)

我可以对任何图像进行很好的分割,例如上面的结果:

在此输入图像描述

第一行是segmentation函数的步骤,第二行是区域,第三行是用作图像顶部蒙版的区域。

(PS是的,情节代码相当难看,但很容易理解和更改)