Python

一文字 カット に挑戦!【 Python OpenCV 】

k.w

Python +OCRの精度向上に挑戦するため、単語を 一文字 に カット しどのような結果になるか試してみました。以外と単語を一文字ずつに分解するのは難しいのですが、画像の特性を理解し、 OpenCV をフル活用すれば実現できました。ちょっとしたコツも公開してます!

スポンサーリンク

できたことから

サンプル画像を一文字ずつの文字の画像の分割を実現しました!

サンプル画像(インプット)

 

結果画像類(アウトプット)

どうですか?うまくできてませんか?

ちょっと力技のところもありますが・・・サンプルコードを共有します!

サンプルコード

/* import設定 */
import os
import glob
import cv2
import configparser as cfg
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from operator import itemgetter

user = os.getlogin()

/* Config設定 */
cfg = cfg.ConfigParser()
cfg.read('config.ini', 'UTF-8')

/* File読み込みフォルダ指定 */
files = glob.glob(cfg.get(user, 'img_folder') +
                  '/cell_cut/*.jpg', recursive=True)

/* File読み込み(複数可) */
for file in files:
    filename = os.path.basename(file)
    print('File:' + filename)
    img = cv2.imread(file, cv2.IMREAD_GRAYSCALE)
    kernel = np.ones((2, 2), np.uint8)
    img = cv2.erode(img, kernel, iterations=1)
    ret, img = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY)

    minlength = int(img.shape[0] * 0.1)
    gap = 0

    lines = []
    lines = cv2.HoughLinesP(img, rho=1, theta=np.pi/360,
                            threshold=1, minLineLength=minlength, maxLineGap=gap)

    line_list = []
    k = 1
    judgelength = int(img.shape[0] * 0.95)
    for line in lines:
        x1, y1, x2, y2 = line[0]
        if abs(x1 - x2) <= k and abs(y1 - y2) >= judgelength:
            blackline = 1
            lineadd_img = cv2.line(img,
                                   (line[0][0], line[0][1]),
                                   (line[0][2], line[0][3]),
                                   (0, 0, 0),
                                   blackline)
            cv2.imwrite(cfg.get(user, 'output_folder') +
                        '/' + filename[:-4] + '_linemod.jpg', lineadd_img)
            line = (x1, y1, x2, y2)
            line_list.append(line)

    line_list.sort(key=itemgetter(0, 1, 2, 3))
    df = pd.DataFrame(line_list)

    line_count = 0
    x1 = line_list[0][0]
    for line in line_list:
        judge_x1 = line[0]
        judge_x2 = line[2]
        if abs(judge_x1 - x1) != 0:
            if abs(x1-judge_x1) < 2:
                x1 = judge_x1
            else:
                line_count = line_count + 1
                x2 = judge_x2
                sep_cut = img[:, x1 + 1:x2]
                w_imgh = np.ones((50, sep_cut.shape[1]), np.uint8) * 255
                sep_cut = cv2.vconcat([w_imgh, sep_cut, w_imgh])
                w_imgw = np.ones((sep_cut.shape[0], 50), np.uint8) * 255
                sep_cut = cv2.hconcat([w_imgw, sep_cut, w_imgw])
                cv2.imwrite(cfg.get(user, 'char_output_folder') + '/' +
                            filename[:-4] + '-' + str(line_count) + '_cut.jpg', sep_cut)
                x1 = judge_x1*/

補足説明

今回は、OpenCVを使い、線を認識する方法を応用しました。
[https://way2se.ringtrees.com/py_cv2-001/]

オリジナル画像ではうまくいかなかった

画像の特徴を理解し、縦方向(分割方向)に線を取得できれば、その座標を使って分割できると考えました。

画像によっては、線を認識してくれないパターンがありました。

 

カットする予定の線を見て分かる通り、全然だめですね。

インプット画像の補正

そこで、画像を補正してみました。
文字を太くし、画像の白黒をはっきりさせる処理(2値化の強化)を実施しました。

文字を太く膨張させる(27行目)
cv2.erode(img, kernel, iterations=1)

2値化の強化する(28行目)
cv2.threshold(img, 150, 255, cv2.THRESH_BINARY)

結果、文字が太くなり、膨張させた結果のグレー部分(薄く膨張されるため)がはっきりし、文字の分別位置が明確に出せました。

 

線を取得し、画像分別判断

40行目~文字間の白い部分を線として取得する処理

59行目~取得した線の文字分割位置の判断。
縦線が、断続するポイントを座標として取得するイメージ。

ここからの応用も

いかがですか?

今回は画像を意図通りにコントロールするためのひとつの手段を紹介しました。

文字を一つずつカットした後、OCR判断でフォルダ振り分けるなど応用にも展開できます
[https://way2se.ringtrees.com/py_comb-001/]

将来的に機械学習にも挑戦したいので、その際の教師データの元になる画像の活用にも使えるのでは?と内心思っています。(データ数が少ないとあまり意味がないですが・・・)

とはいえ、線を取得することが出来るという事実から、文字を分別する。応用することで何でもできるんですね。

みなさんも視点を切り替えて今あるスキルで違うことにトライしてはいかがですか?

ABOUT ME
記事URLをコピーしました