使用 opencv 在 python 中为 OCR 准备适当的图像阈值
Proper image thresholding to prepare it for OCR in python using opencv
我是 opencv 的新手,也是 python 的初学者。
我有这张图片:
我想以某种方式应用适当的阈值,只保留 6 位数字。
更大的图景是,我打算尝试分别对每个数字的图像执行手动 OCR,在每个数字级别上使用 k 最近邻算法 (kNearest.findNearest)
问题是我无法充分清理数字,尤其是带有蓝色水印的“7”数字。
到目前为止我尝试过的步骤如下:
我正在从磁盘读取图像
# IMREAD_UNCHANGED is -1
image = cv2.imread(sys.argv[1], cv2.IMREAD_UNCHANGED)
然后我只保留蓝色通道以去除数字“7”周围的蓝色水印,有效地将其转换为单通道图像
image = image[:,:,0]
# openned with -1 which means as is,
# so the blue channel is the first in BGR
然后我将它乘以一点以增加数字和背景之间的对比度:
image = cv2.multiply(image, 1.5)
最后我执行二进制+Otsu 阈值处理:
_,thressed1 = cv2.threshold(image,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
如您所见,最终结果非常好,除了数字“7”保留了很多噪音。
如何改善最终结果?请尽可能提供图像示例结果,比仅代码片段更容易理解。
为什么不只保留图像中高于特定阈值的值?
像这样:
import cv2
import numpy as np
img = cv2.imread("./a.png")[:,:,0] # the last readable image
new_img = []
for line in img:
new_img.append(np.array(list(map(lambda x: 0 if x < 100 else 255, line))))
new_img = np.array(list(map(lambda x: np.array(x), new_img)))
cv2.imwrite("./b.png", new_img)
看起来很棒:
您可以进一步调整阈值并获得更好的结果。
要彻底去除恼人的邮票似乎并不容易。
你可以做的是通过
将背景强度变平
计算低通图像(高斯滤波器,形态学闭包);过滤器大小应比字符大小稍大;
将原始图像除以低通图像。
那你就可以用大津了
如您所见,结果并不完美。
我尝试了一种与 Yves 在蓝色通道上略有不同的方法:
- 应用中值滤波器 (r=2):
- 使用边缘检测(例如 Sobel 算子):
- 自动阈值化 (Otsu)
- 图像关闭
这种方法似乎可以使输出的噪音少一些。但是,必须解决数字中的漏洞。这可以通过检测完全被白色像素包围的黑色轮廓并简单地用白色填充它们来完成。
你可以尝试用不同的kernels(比如3, 51)对灰度(blur)图像进行medianBlur,划分模糊结果,然后阈值化。像这样:
#!/usr/bin/python3
# 2018/09/23 17:29 (CST)
# (中秋节快乐)
# (Happy Mid-Autumn Festival)
import cv2
import numpy as np
fname = "color.png"
bgray = cv2.imread(fname)[...,0]
blured1 = cv2.medianBlur(bgray,3)
blured2 = cv2.medianBlur(bgray,51)
divided = np.ma.divide(blured1, blured2).data
normed = np.uint8(255*divided/divided.max())
th, threshed = cv2.threshold(normed, 100, 255, cv2.THRESH_OTSU)
dst = np.vstack((bgray, blured1, blured2, normed, threshed))
cv2.imwrite("dst.png", dst)
结果:
我是 opencv 的新手,也是 python 的初学者。
我有这张图片:
我想以某种方式应用适当的阈值,只保留 6 位数字。
更大的图景是,我打算尝试分别对每个数字的图像执行手动 OCR,在每个数字级别上使用 k 最近邻算法 (kNearest.findNearest)
问题是我无法充分清理数字,尤其是带有蓝色水印的“7”数字。
到目前为止我尝试过的步骤如下:
我正在从磁盘读取图像
# IMREAD_UNCHANGED is -1
image = cv2.imread(sys.argv[1], cv2.IMREAD_UNCHANGED)
然后我只保留蓝色通道以去除数字“7”周围的蓝色水印,有效地将其转换为单通道图像
image = image[:,:,0]
# openned with -1 which means as is,
# so the blue channel is the first in BGR
然后我将它乘以一点以增加数字和背景之间的对比度:
image = cv2.multiply(image, 1.5)
最后我执行二进制+Otsu 阈值处理:
_,thressed1 = cv2.threshold(image,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
如您所见,最终结果非常好,除了数字“7”保留了很多噪音。
如何改善最终结果?请尽可能提供图像示例结果,比仅代码片段更容易理解。
为什么不只保留图像中高于特定阈值的值?
像这样:
import cv2
import numpy as np
img = cv2.imread("./a.png")[:,:,0] # the last readable image
new_img = []
for line in img:
new_img.append(np.array(list(map(lambda x: 0 if x < 100 else 255, line))))
new_img = np.array(list(map(lambda x: np.array(x), new_img)))
cv2.imwrite("./b.png", new_img)
看起来很棒:
您可以进一步调整阈值并获得更好的结果。
要彻底去除恼人的邮票似乎并不容易。
你可以做的是通过
将背景强度变平计算低通图像(高斯滤波器,形态学闭包);过滤器大小应比字符大小稍大;
将原始图像除以低通图像。
那你就可以用大津了
如您所见,结果并不完美。
我尝试了一种与 Yves 在蓝色通道上略有不同的方法:
- 应用中值滤波器 (r=2):
- 使用边缘检测(例如 Sobel 算子):
- 自动阈值化 (Otsu)
- 图像关闭
这种方法似乎可以使输出的噪音少一些。但是,必须解决数字中的漏洞。这可以通过检测完全被白色像素包围的黑色轮廓并简单地用白色填充它们来完成。
你可以尝试用不同的kernels(比如3, 51)对灰度(blur)图像进行medianBlur,划分模糊结果,然后阈值化。像这样:
#!/usr/bin/python3
# 2018/09/23 17:29 (CST)
# (中秋节快乐)
# (Happy Mid-Autumn Festival)
import cv2
import numpy as np
fname = "color.png"
bgray = cv2.imread(fname)[...,0]
blured1 = cv2.medianBlur(bgray,3)
blured2 = cv2.medianBlur(bgray,51)
divided = np.ma.divide(blured1, blured2).data
normed = np.uint8(255*divided/divided.max())
th, threshed = cv2.threshold(normed, 100, 255, cv2.THRESH_OTSU)
dst = np.vstack((bgray, blured1, blured2, normed, threshed))
cv2.imwrite("dst.png", dst)
结果: