使用opencv在python中为OCR准备合适的图像阈值

使用opencv在python中为OCR准备合适的图像阈值,python,opencv,image-processing,ocr,image-thresholding,Python,Opencv,Image Processing,Ocr,Image Thresholding,我是opencv的新手,也是python的初学者 我有这样的图像: 我想以某种方式应用适当的阈值,只保留6位数字 更重要的是,我打算尝试分别对每个数字的图像执行手动OCR,在每个数字级别上使用k-最近邻算法(kNearest.findNearest) 问题是我无法充分清理数字,尤其是带有蓝色水印的“7”数字 到目前为止,我尝试的步骤如下: 我正在从磁盘读取图像 # IMREAD_UNCHANGED is -1 image = cv2.imread(sys.argv[1], cv2.IMREAD

我是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)

最后,我执行二进制+大津阈值:

_,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) 
导入cv2
将numpy作为np导入
img=cv2.imread(“./a.png”)[:,:,0]#最后一个可读图像
新的_img=[]
对于img中的行:
新的\u img.append(np.array(列表(如果x<100,则lambda x:0,否则255行)))
new\u img=np.array(列表(map(lambda x:np.array(x),new\u img)))
cv2.imwrite(“./b.png”,新的img)
看起来很棒:


你可能会更多地使用阈值,并获得更好的结果。

要完全删除这个恼人的标记似乎并不容易

你所能做的就是将背景强度展平

  • 计算低通图像(高斯滤波器、形态闭合);过滤器大小应略大于字符大小

  • 将原始图像除以低通图像

那你可以用大津


正如您所见,结果并不完美。

我尝试了一种稍微不同的方法,然后Yves在蓝色通道上:

  • 应用中值滤波器(r=2):

  • 使用边缘检测(例如Sobel算子):

  • 自动阈值(Otsu)

  • 关闭图像


这种方法似乎可以使输出的噪音小一些。然而,我们必须解决数字中的漏洞。这可以通过检测完全被白色像素包围的黑色轮廓并简单地用白色填充来实现

您可以尝试使用不同的内核(如3、51)对灰色(模糊)图像进行中值模糊,对模糊结果进行分割,然后对其设置阈值。大概是这样的:


结果是:


“所以红色通道是RGB中的第一个”。。。。OpenCV默认使用BGR顺序,因此您实际上使用的是蓝色通道,而不是红色通道。您是对的,使用蓝色通道似乎只能更有效地获取蓝色水印的dir。我已经在代码中更正了注释。感谢您如果解决方案需要自动(经常),则无法使用。在很多情况下,全局阈值不起作用。这是一个很好的观点。然而,我检查了,甚至将图像乘以2(再次),产生了一个相当有利的结果:如果解决方案需要自动(经常),这就不能使用。在很多情况下,全局阈值都不起作用。那么,你认为什么是更好的主意呢?@Yves Daoust right,你的解决方案肯定更好。我可能只应该坚持我100%自信的思路。非常好的结果和简单的方法+1@Silencer请你详细说明一下“分割模糊结果”是什么意思?也许是代码片段?我对这方面还很陌生,我还没有完全掌握opencv的概念。将模糊结果与原始图像分割?一次分割两个以上的图像?怎么用?抱歉问了这么多愚蠢的问题。提前谢谢你!即使在一些较难的情况下,消音器也确实起到了作用。非常感谢你!我一直在尝试(几个月来,断断续续地)很多不同的方法,比如布拉德利·罗斯阈值法,但没有成功,或者最终结果更糟,比你的更糟。所以,这是一个真诚的感谢,你是MVP!@Yves也值得称赞,因为从我看来,他建议采用相同的方法,但消音器,先生,您花了时间提供了更好的结果和代码片段。对于所有这些,我很感激+1.您能提供一个关于如何生成低通图像以及如何分割的代码片段吗?这个答案和上面的@消音器有关吗?这个可能有用,但似乎比上面的解决方案要复杂一些,所以也许我会最后试试这个。代码片段将不胜感激。非常感谢。Sobel算子和Otsu阈值可能被Canny边缘检测所取代
#!/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)