Tesseract OCRを使用して画像からテキストを抽出しようとしています。現在、元の入力画像(下図)では、出力の品質は非常に低くなっています(約50%)。したがって、プログラムで(OpenCV、Image magickを使用して)image(keep texts)のすべての線と境界線を削除する方法はありますか?
元の画像:
期待されるイメージ:
OpenCVを使用せず、ターミナルでのImageMagickの1行だけを使用しますが、OpenCVでそれを実行する方法についてのアイデアが得られる場合があります。 ImageMagickは、ほとんどのLinuxディストリビューションにインストールされており、OSXおよびWindowsで使用できます。
コンセプトの核心は、各ピクセルがその左に隣接する100ピクセルとその右に隣接する100ピクセルの中央値に設定される新しい画像を作成することです。このようにして、黒である水平方向の隣接ピクセルが多いピクセル(つまり、水平方向の黒い線)は、出力イメージで白になります。次に、同じ処理を垂直方向に適用して、垂直線を削除します。
ターミナルに入力するコマンドは次のとおりです。
convert input.png \
\( -clone 0 -threshold 50% -negate -statistic median 200x1 \) \
-compose lighten -composite \
\( -clone 0 -threshold 50% -negate -statistic median 1x200 \) \
-composite result.png
最初の行は、元の画像を読み込むことを示しています。
2行目は、いくつかの "aside-processing"を開始し、元の画像をコピーし、しきい値を設定して反転します。次に、両側のすべての隣接ピクセル100の中央値計算されます。
次に、3番目のラインは2番目のラインの結果を取得し、それを元の画像の上に合成し、各位置のピクセルの明るい方(つまり、水平ラインマスクが白くなっているピクセル)を選択します。
次の2行も同じことを行いますが、垂直線の場合は垂直に向けられます。
結果は次のようになります:
このように元の画像と異なる場合は、次のようになります。
convert input.png result.png -compose difference -composite diff.png
もう少し線を削除したい場合は、実際に差分画像を少しぼかして、元の画像に適用できます。もちろん、フィルターの長さやしきい値などで遊ぶこともできます。
ImageMagickでこれを行うより良い方法があります。
ImageMagickには、形状の形態と呼ばれるきちんとした機能があります。これを使用して、表の線のような形状を識別し、それらを削除できます。
convert in.png \
-type Grayscale \
-negate \
-define morphology:compose=darken \
-morphology Thinning 'Rectangle:1x80+0+0<' \
-negate \
out.png
申し込み後
convert in.png -type Grayscale -negate -define morphology:compose=darken -morphology Thinning 'Rectangle:1x80+0+0<' -negate out.png
これは出力画像でした:
同じ問題に直面した。そして、私はより論理的な解決策があり得ると感じています(参照: 表の境界線を抽出 )
//assuming, b_w is the binary image
inv = 255 - b_w
horizontal_img = new_img
vertical_img = new_img
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (100,1))
horizontal_img = cv2.erode(horizontal_img, kernel, iterations=1)
horizontal_img = cv2.dilate(horizontal_img, kernel, iterations=1)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,100))
vertical_img = cv2.erode(vertical_img, kernel, iterations=1)
vertical_img = cv2.dilate(vertical_img, kernel, iterations=1)
mask_img = horizontal_img + vertical_img
no_border = np.bitwise_or(b_w, mask_img)
誰も完全なOpenCVソリューションを投稿していないので、ここに簡単なアプローチがあります
バイナリイメージを取得します。イメージをロードし、グレースケールに変換し、そして 大津のしきい値
水平線を削除します。_cv2.getStructuringElement
_ 次に 輪郭を見つける で水平形状のカーネルを作成します。 = _cv2.drawContours
_ で行を削除します
垂直線を削除します。同じ操作を行いますが、垂直形状のカーネルを使用します
画像を読み込み、グレースケールに変換し、次に 大津のしきい値 でバイナリ画像を取得します
_image = cv2.imread('1.png')
result = image.copy()
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
_
次に、水平カーネルを作成して cv2.getStructuringElement()
で水平線を検出し、 cv2.findContours()
で輪郭を見つけます。水平線を削除するには、 cv2.drawContours()
を使用して、各水平輪郭を白で塗りつぶします。これにより、水平線が効果的に「消去」されます。検出された水平線は緑色です
_# Remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,1))
remove_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(remove_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (255,255,255), 5)
_
同様に、垂直カーネルを作成して、垂直線を削除し、輪郭を見つけ、各垂直輪郭を白で塗りつぶします。検出された垂直線は緑色で強調表示されています
_# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,40))
remove_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(remove_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (255,255,255), 5)
_
水平線と垂直線の両方を白で埋めた後、これが結果です
注:イメージによっては、カーネルサイズの変更が必要になる場合があります。たとえば、長い水平線をキャプチャするには、水平カーネルを_(40, 1)
_から_(80, 1)
_に増やす必要がある場合があります。太い水平線を検出したい場合は、カーネルの幅を増やして_(80, 2)
_とすることができます。さらに、cv2.morphologyEx()
を実行するときに、反復回数を増やすことができます。同様に、垂直カーネルを変更して、多かれ少なかれ垂直線を検出できます。カーネルのサイズを増減する場合、キャプチャする行数が多かれ少なかれ、トレードオフがあります。繰り返しますが、それはすべて入力画像によって異なります
完全性のための完全なコード
_import cv2
image = cv2.imread('1.png')
result = image.copy()
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,1))
remove_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(remove_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (255,255,255), 5)
# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,40))
remove_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(remove_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (255,255,255), 5)
cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.imwrite('result.png', result)
cv2.waitKey()
_
Sobel/Laplacian/CannyのEdge検出アルゴリズムを使用し、Houghの変換を使用してOpenCVのLineを識別し、それらを白く色付けしてLineを削除できます。
laplacian = cv2.Laplacian(img,cv2.CV_8UC1) # Laplacian OR
edges = cv2.Canny(img,80,10,apertureSize = 3) # canny Edge OR
# Output dtype = cv2.CV_8U # Sobel
sobelx8u = cv2.Sobel(img,cv2.CV_8U,1,0,ksize=5)
# Output dtype = cv2.CV_64F. Then take its absolute and convert to cv2.CV_8U
sobelx64f = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)
# Hough's Probabilistic Line Transform
minLineLength = 900
maxLineGap = 100
lines = cv2.HoughLinesP(edges,1,np.pi/180,100,minLineLength,maxLineGap)
for line in lines:
for x1,y1,x2,y2 in line:
cv2.line(img,(x1,y1),(x2,y2),(255,255,255),2)
cv2.imwrite('houghlines.jpg',img)
私はアイデアを持っています。ただし、完全に水平線と垂直線がある場合にのみ機能します。最初にこの画像で2値化を実行できます(まだの場合)。次に、いくつかのしきい値を超える一連の黒いピクセルがあるかどうかを確認しながら、同時に画像の各行を反復処理するコードを記述します。たとえば、100番目のピクセルから150番目のピクセルまでの行に連続した一連の黒い点がある場合、これらのピクセルを白にします。すべての水平線を見つけたら、同じようにして垂直線を取り除くことができます。
ここでの例では、黒のピクセルシーケンスが100番目のピクセルから始まり、150番目のピクセルで終わると考えています。151番目のピクセルに別の黒のピクセルがある場合、そのピクセルも追加する必要があるためです。つまり、行を完全に見つけるようにしてください。
この質問を解決した場合はお知らせください)