なんもわからんな

テキスト検出(SWT)について

以前 気合いで画像から文字だけを抽出する というようなクソ記事を書きましたが,もっと効果的な画像演算があることがわかりました.
おそらくtesseract OCRなどにも使われていると思います.

Stroke Width Transform(SWT)

言語やフォント,スケール,方向に関係なくテキストを検出できる画像演算です.
論文は以下のリンクにあります.

www.microsoft.com

今回はSWTを使ってどの程度検出できるのかやって見たいと思います.
以前と同じ画像を使用し,さを見ます.  

f:id:yakuta55:20180420121106p:plain

上の画像がグレーイスケールにした入力画像で, 下がSWTを適用した後の画像です.
かなり文字が,くっきりと摘出できているかと思います.

今回使用したコードは こちらの方のコードをフォークさせて使わせていただきました.

終わり

ロシア語筆記体の文字のセグメンテーションとかいつかやって見たいですね()

気合いで画像から文字だけを抽出する

現在画像処理をやっているので,その辺の知識を置いていこうと思います

今回使うライブラリです.

import matplotlib.pyplot as plt
from PIL import ImageFont, ImageDraw, Image, ImageOps, ImageFilter 
import numpy as np
from skimage import measure
import cv2
%matplotlib inline

今回使う画像は下のものです.

http://fontsandcolors.com/wp-content/uploads/2016/06/sign_board_maker.jpg

では早速やっていきます. RGBカラーでは処理しづらいので,グレーイスケールに変換します.

sign_board = Image.open("sign_board_maker.jpg")
gsign_board = sign_board.convert('L')

この際私はPillowでやっていますがopenCVを使う場合だと以下のようです

image = cv2.imread('hoge.jpg')
gimage = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

次に閾値を設定して, 2値に変換をします. この処理ではノイズの除去を目的としています.

gimg = np.asarray(gsign_board)
threshold = cv2.threshold(gimg, 200, 255, cv2.THRESH_BINARY)[1]

この時点での結果がこちらです. f:id:yakuta55:20180402105016p:plain

次に文字部分だけを抜き取ります.

labels = measure.label(threshold, neighbors=8, background=0)
mask = np.zeros(threshold.shape, dtype='uint8')

for (i, label) in enumerate(np.unique(labels)):
    if label ==0:
        continue
    labelMask = np.zeros(threshold.shape, dtype="uint8")
    labelMask[labels==label]=255
    numpixels= cv2.countNonZero(labelMask)
    if numpixels > 200 and numpixels < 1000:
        mask = np.add(mask , labelMask)

plt.imshow(mask)

結果が以下です.
measureで2値化下画像から繋がっている要素にラベリングをします.
そのラベルづけされた要素のpixel数が200より大きく1000より小さい場合のみmaskに追加しています.
古典的でダサいですが,これでも割と文字は抽出できます.

f:id:yakuta55:20180402105750p:plain フォントサイズが固定であったりする場合などには有効な手法だと思います.

まとめ

計算リソースに制限がある場合や.実行時間を重視する場合はこのような手法が必要なのかなって思いました.

参考文献

3.3. Scikit-image: 画像処理 — Scipy lecture notes

OpenCV.jp