从图像中分离单个数字以进行 OCR
Isolating individual numbers from image for OCR
单个数字的字符识别方法非常简单。但这是图像只包含一个数字的时候。
当图像包含多个数字时,我们不能使用相同的算法,因为整个位图是不同的。我们如何处理图像以将其拆分,以便我们可以 "modularise" 对每个数字进行 OCR 操作?
按照以下步骤操作:
- 加载图像。
- Select 数字(通过轮廓查找并对字母的面积和高度应用约束以避免错误检测)。这将拆分图像,从而模块化您要执行的 OCR 操作。
- 用于执行识别和分类的简单 K - 最近邻算法。
但是你要执行的是图像分割问题,而不是数字分类问题。就像@VitaliPro 说的。两者都是 OCR 问题,但是(在一个巨大的简化中)第一个问题是 "what character is this",第二个是 "how many characters I have here"。你已经知道如何解决第一个问题,让我们看看第二个通常是如何解决的。
您想将图像分割成字符(在分割中称为"regions"),然后将数字分类应用于每个区域。一种方法是执行 Watershed Segmentation,它使用颜色渐变来区分边缘和区域。
一个简单的分水岭可以用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)
我正在使用 skimage
的 morph
模块的分水岭分割。
大多数情况下,对于分水岭,您应该将区域放在图像的顶部以获得该区域的实际内容,我在上面的代码中没有这样做。然而,数字或大多数文本不需要它,因为它应该是黑白的。
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()
(此示例本身没有 运行,您需要将上面的其他代码示例添加到它的顶部。)
我可以很好地分割任何图像,例如以上结果:
第一行是 segmentation
函数的步骤,第二行是区域,第三行是用作图像顶部遮罩的区域。
(P.S。是的,剧情代码挺难看的,但是好理解改改)
单个数字的字符识别方法非常简单。但这是图像只包含一个数字的时候。
当图像包含多个数字时,我们不能使用相同的算法,因为整个位图是不同的。我们如何处理图像以将其拆分,以便我们可以 "modularise" 对每个数字进行 OCR 操作?
按照以下步骤操作:
- 加载图像。
- Select 数字(通过轮廓查找并对字母的面积和高度应用约束以避免错误检测)。这将拆分图像,从而模块化您要执行的 OCR 操作。
- 用于执行识别和分类的简单 K - 最近邻算法。
但是你要执行的是图像分割问题,而不是数字分类问题。就像@VitaliPro 说的。两者都是 OCR 问题,但是(在一个巨大的简化中)第一个问题是 "what character is this",第二个是 "how many characters I have here"。你已经知道如何解决第一个问题,让我们看看第二个通常是如何解决的。
您想将图像分割成字符(在分割中称为"regions"),然后将数字分类应用于每个区域。一种方法是执行 Watershed Segmentation,它使用颜色渐变来区分边缘和区域。
一个简单的分水岭可以用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)
我正在使用 skimage
的 morph
模块的分水岭分割。
大多数情况下,对于分水岭,您应该将区域放在图像的顶部以获得该区域的实际内容,我在上面的代码中没有这样做。然而,数字或大多数文本不需要它,因为它应该是黑白的。
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()
(此示例本身没有 运行,您需要将上面的其他代码示例添加到它的顶部。)
我可以很好地分割任何图像,例如以上结果:
第一行是 segmentation
函数的步骤,第二行是区域,第三行是用作图像顶部遮罩的区域。
(P.S。是的,剧情代码挺难看的,但是好理解改改)