ここに表のある画像があります。右側の列の背景はノイズで満たされています
ノイズのあるエリアを検出する方法は?ノイズのある部分に何らかのフィルターを適用したいのは、OCRを行う必要があり、どのようなフィルターでも全体的な認識が低下するためです
また、画像の背景ノイズを除去するにはどのようなフィルターが最適ですか?
言ったように、画像に対してOCRを行う必要があります
OpenCVでいくつかのフィルター/操作を試しましたが、かなりうまくいくようです。
ステップ1:Dilate画像-
kernel = np.ones((5, 5), np.uint8)
cv2.dilate(img, kernel, iterations = 1)
ご覧のとおり、ノイズはなくなりましたが、キャラクターは非常に軽いため、画像を浸食しました。
ステップ2:Erode画像-
kernel = np.ones((5, 5), np.uint8)
cv2.erode(img, kernel, iterations = 1)
ご覧のとおり、ノイズはなくなりましたが、他の列の一部の文字は壊れています。ノイズの多い列でのみこれらの操作を実行することをお勧めします。 HoughLines を使用して、最後の列を見つけることができます。その後、その列のみを抽出し、膨張+収縮を実行し、これを元の画像の対応する列に置き換えることができます。さらに、膨張+収縮は、実際にはclosingと呼ばれる操作です。これを使用して直接呼び出すことができます-
cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
@Ermlgが示唆したように、3のカーネルを持つmedianBlurも見事に機能します。
cv2.medianBlur(img, 3)
代替ステップ
ご覧のとおり、これらのフィルターはすべて機能しますが、ノイズのある部分にのみこれらのフィルターを実装する方が良いでしょう。これを行うには、次を使用します。
edges = cv2.Canny(img, 50, 150, apertureSize = 3) // img is gray here
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 100, 1000, 50) // last two arguments are minimum line length and max gap between two lines respectively.
for line in lines:
for x1, y1, x2, y2 in line:
print x1, y1
// This gives the start coordinates for all the lines. You should take the x value which is between (0.75 * w, w) where w is the width of the entire image. This will give you essentially **(x1, y1) = (1896, 766)**
次に、この部分のみを次のように抽出できます。
extract = img[y1:h, x1:w] // w, h are width and height of the image
次に、このイメージにフィルター(中央値または終了値)を実装します。ノイズを除去した後、元の画像のぼやけた部分の代わりにこのフィルタリングされた画像を配置する必要があります。 image [y1:h、x1:w] =中央値
これはC++では簡単です。
extract.copyTo(img, new Rect(x1, y1, w - x1, h - y1))
代替方法による最終結果
私のソリューションは、4つのステップで結果の画像を取得するためのしきい値処理に基づいています。
OpenCV 3.2.0
_で画像を読み取ります。GaussianBlur()
を適用して、特にグレー色の領域の画像を滑らかにします。コードは_Python 2.7
_にあります。簡単に_C++
_に変更できます。
_import numpy as np
import cv2
import matplotlib.pyplot as plt
%matplotlib inline
# read Danish doc image
img = cv2.imread('./imagesStackoverflow/danish_invoice.png')
# apply GaussianBlur to smooth image
blur = cv2.GaussianBlur(img,(5,3), 1)
# threshhold gray region to white (255,255, 255) and sets the rest to black(0,0,0)
mask=cv2.inRange(blur,(0,0,0),(150,150,150))
# invert the image to have text black-in-white
res = 255 - mask
plt.figure(1)
plt.subplot(121), plt.imshow(img[:,:,::-1]), plt.title('original')
plt.subplot(122), plt.imshow(blur, cmap='gray'), plt.title('blurred')
plt.figure(2)
plt.subplot(121), plt.imshow(mask, cmap='gray'), plt.title('masked')
plt.subplot(122), plt.imshow(res, cmap='gray'), plt.title('result')
plt.show()
_
以下は、参照用のコードによってプロットされた画像です。
これは、2197 x 3218ピクセルでの結果画像です。
私が知っているように、メジアンフィルターはノイズを減らすための最良の解決策です。 3x3ウィンドウでメディアンフィルターを使用することをお勧めします。関数 cv :: medianBlur() を参照してください。
ただし、OCRと同時にノイズフィルターを使用する場合は注意してください。そのため、認識精度が低下する可能性があります。
また、関数のペア(cv :: erode()とcv :: dilate())を使用することをお勧めします。しかし、ウィンドウ3x3でcv :: medianBlur()を使用するのが最善の解決策になるかどうかはわかりません。
中央値ぼかし(おそらく5 * 5カーネル)を使用します。
oCRを画像に適用する予定の場合。次のことをお勧めします。
より高速なソリューション:
処理時間が問題にならない場合、この場合の非常に効果的な方法は、すべての黒の連結成分を計算し、数ピクセル未満の成分を削除することです。ノイズの多いドット(有効なコンポーネントに触れているドットを除く)はすべて削除されますが、すべての文字とドキュメント構造(行など)は保持されます。
使用する関数は connectedComponentWithStats (おそらくネガティブイメージを生成する必要がある前に、 threshold を使用するTHRESH_BINARY_INV
はこの場合機能します)、小さな接続コンポーネントが見つかった場所に白い長方形を描画します。
実際、このメソッドは、特定の最小サイズと最大サイズの連結成分として定義され、特定の範囲のアスペクト比を持つ文字を見つけるために使用できます。
私はすでに同じ問題に直面しており、最良の解決策を得ました。ソース画像をグレースケール画像に変換し、fastNlMeanDenoising関数を適用してからthresholdを適用します。
このように-fastNlMeansDenoising(gray、dst、3.0,21,7); threshold(dst、finaldst、150,255、THRESH_BINARY);
ALSOを使用すると、背景ノイズ画像に応じてしきい値を調整できます。 eg- threshold(dst、finaldst、200,255 、THRESH_BINARY);
注-列線が削除された場合...ソース画像から列線のマスクを取得し、AND、OR、XORなどのBITWISE操作を使用して、ノイズ除去された結果画像に適用できます。
OCR検出を損なう可能性のあるピクセルを削除することを非常に心配している場合。アーティファクトを追加することなく、可能な限りオリジナルに対して純粋になります。次に、blobフィルターを作成する必要があります。そして、nピクセル程度より小さいblobを削除します。
コードを書くつもりはありませんが、openCVは使用しませんが(速度の理由から独自のマルチスレッドblobfilterを作成しました)、自分でこれを使用するのでこれはうまく機能します。申し訳ありませんが、ここでコードを共有できません。方法を説明するだけです。