web-dev-qa-db-ja.com

さまざまな色とフォントのテキストを含む画像のOCRを改善するにはどうすればよいですか?

Google Vision API を使用して一部の画像からテキストを抽出していますが、運が悪く結果の正確性(信頼性)を向上させようとしています。

画像を元の画像から変更するたびに、一部の文字の検出精度が低下します。

異なる単語に複数の色があるように問題を分離しました。たとえば、赤の単語は他の単語よりも頻繁に間違った結果を持っていることがわかります。

例:

グレースケールまたは白黒からの画像のいくつかのバリエーション

Original Image

enter image description here

enter image description here

enter image description here

enter image description here

ほとんどのアルゴリズムがそれを期待しているので、これをよりうまく機能させるためにどのようなアイデアを試すことができますか?特に、テキストの色を均一な色または白い背景の上でただ黒に変更しますか?

私がすでに試したいくつかのアイデア、そしていくつかのしきい値設定。

dimg = ImageOps.grayscale(im)
cimg = ImageOps.invert(dimg)

contrast = ImageEnhance.Contrast(dimg)
eimg = contrast.enhance(1)

sharp = ImageEnhance.Sharpness(dimg)
eimg = sharp.enhance(1)
7
RaedMarji

ほとんどすべての標準的な手順を試しました。シャープネスフィルターのようないくつかのPIL組み込みフィルターを試すことをお勧めします。 RGB画像にシャープネスとコントラストを適用してから、ビナライズします。おそらく、Image.split()とImage.merge()を使用して、各色を個別にビナライズしてから、それらを元に戻します。または、画像をYUVに変換してから、Yチャネルだけを使用してさらに処理します。また、モノクロの背景がない場合は、背景の減算を実行することを検討してください。

スキャンされたテキストを検出するときにtesseractが好むのはフレームが削除されるため、画像から文字以外のスペースをできるだけ破壊しようとすることができます。 (ただし、画像のサイズを維持する必要がある場合があるため、白色に置き換える必要があります)。テッセラクトも直線が好きです。したがって、テキストが斜めに記録されている場合は、ある程度のデスキューが必要になることがあります。また、画像のサイズを元のサイズの2倍に変更すると、Tesseractでより良い結果が得られる場合があります。

Google Visionはテッセラクトまたはその一部を使用しているのではないかと思いますが、それ以外の前処理で何ができるかはわかりません。したがって、ここでの私のアドバイスのいくつかは実際にはすでに実装されている可能性があり、それらを実行することは不必要で反復的です。

1
Dalen

これは完全な解決策ではありませんが、何かより良いものになるかもしれません。

データを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')
0
John_Sharp1318

これについてもう少しコンテキストが必要です。

  1. Google Vision APIを何回呼び出しますか?ストリーム全体でこれを行う場合は、おそらく有料サブスクリプションを取得する必要があります。
  2. このデータをどのように処理しますか? OCRはどの程度正確である必要がありますか?
  3. このスナップショットを別のTwitchストリームから取得し、ストリーマーのビデオ圧縮とネットワーク接続を処理すると仮定すると、かなりぼやけたスナップショットが取得されるため、OCRはかなり難しくなります。

ビデオ圧縮のために画像がぼやけすぎているため、品質を改善するために画像を前処理しても、正確なOCRに十分な画像品質が得られない場合があります。 OCRを使用している場合は、次の方法を試してみてください。

  1. 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)
    

enter image description here

  1. フィルターで赤いテキスト値のみを抽出し、それを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()
    

ただし、このフィルタリングを行っても、赤はうまく抽出されません。

enter image description here

enter image description here

  1. (1.)と(2.)で取得した画像を追加します。

    combined_image = binarized + red_binarized
    

enter image description here

  1. (3)でOCRを実行する
0

画像を複数回前処理し、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が必要ですが、同じ原理が他のイメージパッケージにも適用されます。

0
darrahts

私は肉屋のソリューションしか提供できず、維持するのが悪夢の可能性があります。

私自身の非常に限られたシナリオでは、他のいくつかのOCRエンジンが失敗したか、許容できない実行時間があったかのように機能しました。

私の前提条件:

  • 画面のどの領域にテキストが表示されるかを正確に知っていました。
  • 使用するフォントと色を正確に知っていました。
  • テキストは半透明だったので、下にある画像が邪魔になり、起動する可変画像でした。
  • 平均的なフレームへのテキストの変更を確実に検出して、干渉を減らすことができませんでした。

私がしたこと:-各キャラクターのカーニング幅を測定しました。私はA-Za-z0-9と、心配する必要のある句読文字の束しか持っていませんでした。 -プログラムは(0,0)の位置から開始し、平均色を測定して色を決定し、その色のすべての使用可能なフォントの文字から生成されたビットマップのセット全体にアクセスします。次に、画面上の対応する長方形に最も近い長方形を特定し、次の長方形に進みます。

(数か月後、より多くのパフォーマンスが必要になるため、最も可能性の高い文字を最初にテストするために、さまざまな確率行列を追加しました)。

最終的に、結果のCプログラムは、ビデオストリームから100%の精度でリアルタイムで字幕を読み取ることができました。

0
LSerni