Google Vision API を使用して一部の画像からテキストを抽出していますが、運が悪く結果の正確性(信頼性)を向上させようとしています。
画像を元の画像から変更するたびに、一部の文字の検出精度が低下します。
異なる単語に複数の色があるように問題を分離しました。たとえば、赤の単語は他の単語よりも頻繁に間違った結果を持っていることがわかります。
例:
グレースケールまたは白黒からの画像のいくつかのバリエーション
ほとんどのアルゴリズムがそれを期待しているので、これをよりうまく機能させるためにどのようなアイデアを試すことができますか?特に、テキストの色を均一な色または白い背景の上でただ黒に変更しますか?
私がすでに試したいくつかのアイデア、そしていくつかのしきい値設定。
dimg = ImageOps.grayscale(im)
cimg = ImageOps.invert(dimg)
contrast = ImageEnhance.Contrast(dimg)
eimg = contrast.enhance(1)
sharp = ImageEnhance.Sharpness(dimg)
eimg = sharp.enhance(1)
ほとんどすべての標準的な手順を試しました。シャープネスフィルターのようないくつかのPIL組み込みフィルターを試すことをお勧めします。 RGB画像にシャープネスとコントラストを適用してから、ビナライズします。おそらく、Image.split()とImage.merge()を使用して、各色を個別にビナライズしてから、それらを元に戻します。または、画像をYUVに変換してから、Yチャネルだけを使用してさらに処理します。また、モノクロの背景がない場合は、背景の減算を実行することを検討してください。
スキャンされたテキストを検出するときにtesseractが好むのはフレームが削除されるため、画像から文字以外のスペースをできるだけ破壊しようとすることができます。 (ただし、画像のサイズを維持する必要がある場合があるため、白色に置き換える必要があります)。テッセラクトも直線が好きです。したがって、テキストが斜めに記録されている場合は、ある程度のデスキューが必要になることがあります。また、画像のサイズを元のサイズの2倍に変更すると、Tesseractでより良い結果が得られる場合があります。
Google Visionはテッセラクトまたはその一部を使用しているのではないかと思いますが、それ以外の前処理で何ができるかはわかりません。したがって、ここでの私のアドバイスのいくつかは実際にはすでに実装されている可能性があり、それらを実行することは不必要で反復的です。
これは完全な解決策ではありませんが、何かより良いものになるかもしれません。
データをBGR(またはRGB)からCIE-La bに変換することにより、グレースケール画像をカラーチャネルa *とb *の加重和として処理できます。このグレースケール画像は、テキストのカラー領域を強調します。しかし、しきい値を調整すると、このグレースケール画像から、元の画像の色付きの単語をセグメント化し、Lチャネルのしきい値から他の単語を取得できます。 2つのセグメンテーションイメージをマージするには、ビット単位のAND演算子で十分です。
より良いコントラストの画像を作成できる場合、最後のステップは輪郭に基づく塗りつぶしです。それには、関数 'cv2.findContours'のRETR_FLOODFILL
をご覧ください。他のパッケージからの他の穴ファイリング機能もその目的に適合します。
これが私のアイデアの最初の部分を示すコードです。
import cv2
import numpy as np
from matplotlib import pyplot as plt
I = cv2.UMat(cv2.imread('/home/smile/QSKN.png',cv2.IMREAD_ANYCOLOR))
Lab = cv2.cvtColor(I,cv2.COLOR_BGR2Lab)
L,a,b = cv2.split(Lab)
Ig = cv2.addWeighted(cv2.UMat(a),0.5,cv2.UMat(b),0.5,0,dtype=cv2.CV_32F)
Ig = cv2.normalize(Ig,None,0.,255.,cv2.NORM_MINMAX,cv2.CV_8U)
#k = np.ones((3,3),np.float32)
#k[2,2] = 0
#k*=-1
#
#Ig = cv2.filter2D(Ig,cv2.CV_32F,k)
#Ig = cv2.absdiff(Ig,0)
#Ig = cv2.normalize(Ig,None,0.,255.,cv2.NORM_MINMAX,cv2.CV_8U)
_, Ib = cv2.threshold(Ig,0.,255.,cv2.THRESH_OTSU)
_, Lb = cv2.threshold(cv2.UMat(L),0.,255.,cv2.THRESH_OTSU)
_, ax = plt.subplots(2,2)
ax[0,0].imshow(Ig.get(),cmap='gray')
ax[0,1].imshow(L,cmap='gray')
ax[1,0].imshow(Ib.get(),cmap='gray')
ax[1,1].imshow(Lb.get(),cmap='gray')
これについてもう少しコンテキストが必要です。
ビデオ圧縮のために画像がぼやけすぎているため、品質を改善するために画像を前処理しても、正確なOCRに十分な画像品質が得られない場合があります。 OCRを使用している場合は、次の方法を試してみてください。
2値化した画像のように、画像を2値化して、赤以外のテキストを白と背景を黒にします。
from PIL import Image
def binarize_image(im, threshold):
"""Binarize an image."""
image = im.convert('L') # convert image to monochrome
bin_im = image.point(lambda p: p > threshold and 255)
return bin_im
im = Image.open("game_text.JPG")
binarized = binarize_image(im, 100)
フィルターで赤いテキスト値のみを抽出し、それを2値化します。
import cv2
from matplotlib import pyplot as plt
lower = [15, 15, 100]
upper = [50, 60, 200]
lower = np.array(lower, dtype = "uint8")
upper = np.array(upper, dtype = "uint8")
mask = cv2.inRange(im, lower, upper)
red_binarized = cv2.bitwise_and(im, im, mask = mask)
plt.imshow(cv2.cvtColor(red_binarized, cv2.COLOR_BGR2RGB))
plt.show()
ただし、このフィルタリングを行っても、赤はうまく抽出されません。
(1.)と(2.)で取得した画像を追加します。
combined_image = binarized + red_binarized
画像を複数回前処理し、bitwise_or
操作を使用して結果を組み合わせる必要があります。色を抽出するには、次を使用できます
import cv2
boundaries = [ #BGR colorspace for opencv, *not* RGB
([15, 15, 100], [50, 60, 200]), #red
([85, 30, 2], [220, 90, 50]), #blue
([25, 145, 190], [65, 175, 250]), #yellow
]
for (low, high) in boundaries:
low = np.array(low, dtype = "uint8")
high = np.array(high, dtype = "uint8")
# find the colors within the specified boundaries and apply
# the mask
mask = cv2.inRange(image, low, high)
bitWise = cv2.bitwise_and(image, image, mask=mask)
#now here is the image masked with the specific color boundary...
マスクされたイメージを取得したら、「最終的な」イメージになる別のbitwise_or
操作を実行して、本質的にこのマスクを追加できます。
ただし、この特定の実装にはopencvが必要ですが、同じ原理が他のイメージパッケージにも適用されます。
私は肉屋のソリューションしか提供できず、維持するのが悪夢の可能性があります。
私自身の非常に限られたシナリオでは、他のいくつかのOCRエンジンが失敗したか、許容できない実行時間があったかのように機能しました。
私の前提条件:
私がしたこと:-各キャラクターのカーニング幅を測定しました。私はA-Za-z0-9と、心配する必要のある句読文字の束しか持っていませんでした。 -プログラムは(0,0)の位置から開始し、平均色を測定して色を決定し、その色のすべての使用可能なフォントの文字から生成されたビットマップのセット全体にアクセスします。次に、画面上の対応する長方形に最も近い長方形を特定し、次の長方形に進みます。
(数か月後、より多くのパフォーマンスが必要になるため、最も可能性の高い文字を最初にテストするために、さまざまな確率行列を追加しました)。
最終的に、結果のCプログラムは、ビデオストリームから100%の精度でリアルタイムで字幕を読み取ることができました。