问题描述
我正在尝试使用 Python/OpenCV 去除日期周围的灰色“噪音”,以帮助 OCR
(光学字符识别)识别日期。
原始图像如下所示:https://static.mothership.sg/1/2017/03/10-Feb-MC-1.jpg
我尝试的 python 脚本如下所示。但是,我还有其他类似的图像,其中对比度或照明条件各不相同。
import cv2
import numpy as np
img = cv2.imread("mc.jpeg")
img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
alpha = 3.5
beta = -2
new = alpha * img + beta
new = np.clip(new,255).astype(np.uint8)
cv2.imwrite("cleaned.png",new)
我也尝试过 Thresholding
和/或 adaptiveThresholding
,有一段时间,我能够将日期与灰色背景分开。有时这是非常具有挑战性的。我想知道有没有自动确定阈值的方法?
以下是我希望实现的目标。
解决方法
Otsu 的二值化会根据图像直方图自动计算阈值。
# Otsu's thresholding after Gaussian filtering
blur = cv2.GaussianBlur(img,(5,5),0)
ret,Otsu = cv2.threshold(blur,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
cv2.imwrite("Otsu's_thresholding",Otsu)
看到这个link
,您可以尝试构建背景模型,然后通过该模型对每个输入像素进行加权。 输出增益在图像的大部分时间应该是相对恒定的。这些是此方法的步骤:
- 应用柔和的中值模糊滤镜以去除小噪点
- 通过局部最大值获取背景模型。应用一个非常强大的
close
操作,带有一个大structuring element
(我使用的是大小为15
的矩形核) - 通过在每个局部最大像素之间划分
255
来执行增益调整。用每个输入图像像素加权这个值。 - 你应该得到一个很好的图像,其中背景照明几乎标准化,
threshold
这个图像得到文本的二进制掩码
这是代码:
import numpy as np
import cv2
# image path
path = "C:/opencvImages/sheet01.jpg"
# Read an image in default mode:
inputImage = cv2.imread(path)
# Remove small noise via median:
filterSize = 5
imageMedian = cv2.medianBlur(inputImage,filterSize)
# Get local maximum:
kernelSize = 15
maxKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(kernelSize,kernelSize))
localMax = cv2.morphologyEx(imageMedian,cv2.MORPH_CLOSE,maxKernel,None,1,cv2.BORDER_REFLECT101)
# Adjust image gain:
height,width,depth = localMax.shape
# Create output Mat:
outputImage = np.zeros(shape=[height,depth],dtype=np.uint8)
for i in range(0,height):
for j in range(0,width):
# Get current BGR pixels:
v1 = inputImage[i,j]
v2 = localMax[i,j]
# Gain adjust:
tempArray = []
for c in range(0,3):
currentPixel = v2[c]
if currentPixel != 0:
gain = 255 / v2[c]
gain = v1[c] * gain
else:
gain = 0
# Gain set and clamp:
tempArray.append(np.clip(gain,255))
# Set pixel vec to out image:
outputImage[i,j] = tempArray
# Convert RGB to grayscale:
grayscaleImage = cv2.cvtColor(outputImage,cv2.COLOR_BGR2GRAY)
# Threshold:
threshValue = 110
_,binaryImage = cv2.threshold(grayscaleImage,threshValue,cv2.THRESH_BINARY)
# Write image:
imageFilename = "C:/opencvImages/binaryMask2.png"
cv2.imwrite(imageFilename,binaryImage)
我得到以下测试完整图像的结果:

以及裁剪后的文本:

请注意,增益调整操作未矢量化。脚本很慢,主要是因为我从 Python
开始,并且不知道正确的 Numpy
语法来加速此操作。我已经使用 C++
很长时间了,所以请随时进一步改进代码。
编辑:
请注意,您的结果只能与输入的质量一样好。查看您的输入并问自己“这对自动化流程来说是一个好的输入吗?”(自动化流程通常不是很聪明)。您发布的第二张图片非常质量很差。不仅模糊而且分辨率低并且有压缩伪影。所有这些因素都会阻碍自动化处理。
话虽如此,您可以在原版中加入以下改进:
尝试 normalize
灰度输出的亮度对比度:
grayscaleImage = np.uint8(cv2.normalize(grayscaleImage,grayscaleImage,cv2.NORM_MINMAX))
您的灰度图像来自:

为此:

有点暗,对比度有所提高。让我们尝试通过 Otsu thresholding
自动计算最佳阈值:
threshValue,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
它让你知道:

但是,如果我们将 bias
添加到 Otsu 的阈值,我们可以调整结果,如下所示:
threshValue,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
bias = 0.9
threshValue = bias * threshValue
_,cv2.THRESH_BINARY)

这是使用这种方法可以获得的最佳图像质量。 如果您觉得这些建议和技巧有用,请至少为我的回答点赞。