使用OpenCV清理文本图像以进行OCR阅读

5
我收到了一些需要处理的图像,以便从中OCR出一些信息。以下是原始图像: 原始图像1 原始图像1 原始图像2 原始图像2 原始图像3 原始图像3 原始图像4 原始图像4 使用以下代码进行处理:
img = cv2.imread('original_1.jpg', 0) 
ret,thresh = cv2.threshold(img,55,255,cv2.THRESH_BINARY)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_RECT,(2,2)))
cv2.imwrite('result_1.jpg', opening)

我得到了以下结果: 结果1 结果1 结果2 结果2 结果3 结果3 结果4 结果4 可以看出,有些图片在OCR阅读时效果很好,而其他一些则仍保留了一些背景噪声。
是否有任何建议如何清除背景噪声?

阅读有关ImproveQuality的内容。 - stovfl
4个回答

3
MH304的答案非常好,简单明了。如果您无法使用形态学或模糊来获得更清晰的图像,请考虑使用“区域过滤器”。也就是说,过滤掉每个没有展现最小面积的斑点。
使用OpenCV的connectedComponentsWithStats函数,这里是一个非常基本的区域过滤器的C++实现:
cv::Mat outputLabels, stats, img_color, centroids;

int numberofComponents = cv::connectedComponentsWithStats(bwImage, outputLabels, 
stats, centroids, connectivity);

std::vector<cv::Vec3b> colors(numberofComponents+1);
colors[i] = cv::Vec3b(rand()%256, rand()%256, rand()%256);

//do not count the original background-> label = 0:
colors[0] = cv::Vec3b(0,0,0);

//Area threshold:
int minArea = 10; //10 px

for( int i = 1; i <= numberofComponents; i++ ) {

    //get the area of the current blob:
    auto blobArea = stats.at<int>(i-1, cv::CC_STAT_AREA);

    //apply the area filter:
    if ( blobArea < minArea )
    {
        //filter blob below minimum area:
        //small regions are painted with (ridiculous) pink color
        colors[i-1] = cv::Vec3b(248,48,213);

    }

}

使用面积过滤器,在您最嘈杂的图像上得到了以下结果:

enter image description here

**附加信息:

基本上,算法步骤如下:

  • 将二进制图像传递给connectedComponentsWithStats。该函数将计算连通组件的数量、标签矩阵和一个包含统计信息(包括斑点面积)的附加矩阵。

  • 准备一个大小为“numberOfcomponents”的颜色向量,这将有助于可视化我们实际过滤的斑点。颜色由rand函数随机生成。从0-255范围内选择3个值来表示每个像素:BGR。

  • 考虑到背景是黑色的,因此忽略该“连通分量”及其颜色(黑色)。

  • 设置面积阈值。所有小于此面积的斑点或像素都将被涂成(可笑的)粉色。

  • 循环遍历所有找到的连通组件(斑点),通过统计矩阵检索当前斑点的面积并将其与面积阈值进行比较。

  • 如果面积低于阈值,则将斑点涂成粉色(在此情况下,但通常您希望涂成黑色)。


我认为这会有效,只是我在将其转换为Python时遇到了一些问题。 - SteelMasimo
@SteelMasimo 我添加了一些指针来帮助你浏览算法。不幸的是,我无法帮助你进行Python转换,因为我使用的是C++,但希望这些额外的信息能帮助你移植算法! - stateMachine
我成功地实现了Python版本,并且它表现得非常出色。感谢您的帮助! - SteelMasimo

2
这是一个完整的Python解决方案,基于@eldesgraciado提供的指导方向。
此代码假定您已经使用正确二值化的白底黑色图像(例如在灰度转换、黑帽变形和Otsu阈值处理后)工作 - OpenCV文档建议在应用形态学操作和类似操作时使用具有白色前景的二值化图像。
num_comps, labeled_pixels, comp_stats, comp_centroids = \
    cv2.connectedComponentsWithStats(thresh_image, connectivity=4)
min_comp_area = 10 # pixels
# get the indices/labels of the remaining components based on the area stat
# (skip the background component at index 0)
remaining_comp_labels = [i for i in range(1, num_comps) if comp_stats[i][4] >= min_comp_area]
# filter the labeled pixels based on the remaining labels, 
# assign pixel intensity to 255 (uint8) for the remaining pixels
clean_img = np.where(np.isin(labeled_pixels,remaining_comp_labels)==True,255,0).astype('uint8')

这种解决方案的优点是它允许您过滤掉噪声,而不会对可能已经受损的字符产生负面影响。
我处理的是有缺陷扫描件,如合并字符和字符侵蚀等不良效果,我通过艰难的方式发现,即使使用3x3内核和一次迭代进行看似无害的开运算,也会导致一些字符退化(尽管非常有效地消除了字符周围的噪声)。
因此,如果字符质量允许,整个图像的钝化清理操作(例如模糊、开运算、闭运算)是可以接受的,但如果不行,则应首先进行此操作。
附注:还有一件事——在处理文本图像时,不应使用JPEG这样的有损格式,而应改用PNG这样的无损格式。

0

使用这个,它会去除噪音:

cv2.bilateralFilter(img,9,75,75)


0
一个小中值滤波器给我带来了这个结果:

enter image description here

代码(Opencv C++):

Mat im = imread("E:/4.jpg",0);
medianBlur(im, im, 3);
threshold(im, im, 70, 255, THRESH_BINARY_INV);
imshow("1", im);
waitKey(0);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接