簡體   English   中英

如何使用 Python OpenCV 從 OCR 圖像中去除噪聲偽影?

[英]How to remove noise artifacts from an image for OCR with Python OpenCV?

我有包含數字的圖像子集。 Tesseract 為 OCR 讀取每個子集。 不幸的是,對於某些圖像,原始圖像的裁剪不是最佳的。

在此處輸入圖像描述

因此,圖像頂部和底部存在一些偽影/殘留物,並妨礙 Tesseract 識別圖像上的字符。 然后我想擺脫這些工件並得到類似的結果:

在此處輸入圖像描述

首先,我考慮了一種簡單的方法:我將第一行像素設置為參考:如果沿 x 軸發現偽影(即,如果圖像被二值化,則為白色像素),我沿 y 軸將其移除,直到下一個黑色像素。 這種方法的代碼如下:

import cv2
inp = cv2.imread("testing_file.tif")
inp = cv2.cvtColor(inp, cv2.COLOR_BGR2GRAY)
_,inp = cv2.threshold(inp, 150, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

ax = inp.shape[1]
ay = inp.shape[0]

out = inp.copy()
for i in range(ax):
    j = 0
    while j in range(ay):
        if out[j,i] == 255:
            out[j,i] = 0
        else:
            break
        j+=1

out = cv2.bitwise_not(out)    
cv2.imwrite('output.png',out)

但結果一點都不好:

在此處輸入圖像描述

然后我偶然發現了來自 scipy 的 flood_fill function ( 這里),但發現它太耗時而且效率不高。 一個類似的問題被問到了這里,但沒有太大幫助。 也許可以考慮k-最近鄰方法? 我還發現在某些條件下合並相鄰像素的方法稱為增長方法,其中單鏈接是最常見的( 此處)。

你會推薦什么來去除上下偽影?

這是一個簡單的方法:

  • 將圖像轉換為灰度
  • 大津閾值獲取二值圖像
  • Cerate專用卧式kernel並擴張
  • 檢測水平線,排序最大輪廓,並在蒙版上繪制
  • 按位與

轉換為灰度后,我們用Otsu的閾值得到二值圖像

在此處輸入圖像描述

# Read in image, convert to grayscale, and Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

接下來我們創建一個長的水平 kernel 並擴張以將數字連接在一起

在此處輸入圖像描述

# Create special horizontal kernel and dilate 
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (70,1))
dilate = cv2.dilate(thresh, horizontal_kernel, iterations=1)

從這里我們檢測水平線並排序最大的輪廓。 這個想法是最大的輪廓將是數字的中間部分,其中數字都是“完整的”。 任何較小的輪廓都將是部分或截斷的數字,因此我們在這里將它們過濾掉。 我們把這個最大的輪廓畫到一個面具上

在此處輸入圖像描述

# Detect horizontal lines, sort for largest contour, and draw on mask
mask = np.zeros(image.shape, dtype=np.uint8)
detected_lines = cv2.morphologyEx(dilate, cv2.MORPH_OPEN, horizontal_kernel, iterations=1)
cnts = cv2.findContours(detected_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
for c in cnts:
    cv2.drawContours(mask, [c], -1, (255,255,255), -1)
    break

現在我們有了所需數字的輪廓,我們只需按位 - 並使用我們的原始圖像並將背景着色為白色以獲得我們的結果

在此處輸入圖像描述

# Bitwise-and to get result and color background white
mask = cv2.cvtColor(mask,cv2.COLOR_BGR2GRAY)
result = cv2.bitwise_and(image,image,mask=mask)
result[mask==0] = (255,255,255)

完整的完整代碼

import cv2
import numpy as np

# Read in image, convert to grayscale, and Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Create special horizontal kernel and dilate 
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (70,1))
dilate = cv2.dilate(thresh, horizontal_kernel, iterations=1)

# Detect horizontal lines, sort for largest contour, and draw on mask
mask = np.zeros(image.shape, dtype=np.uint8)
detected_lines = cv2.morphologyEx(dilate, cv2.MORPH_OPEN, horizontal_kernel, iterations=1)
cnts = cv2.findContours(detected_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
for c in cnts:
    cv2.drawContours(mask, [c], -1, (255,255,255), -1)
    break

# Bitwise-and to get result and color background white
mask = cv2.cvtColor(mask,cv2.COLOR_BGR2GRAY)
result = cv2.bitwise_and(image,image,mask=mask)
result[mask==0] = (255,255,255)

cv2.imshow('thresh', thresh)
cv2.imshow('dilate', dilate)
cv2.imshow('result', result)
cv2.waitKey()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM