画像OCR(連続文字の認識)

数字がどこに書かれているかを見つける処理を実装してみます。
まずは数字が10個表示されている下記の画像を入力ファイルとします。
10個の数字(numbers.png)

実装する処理は下記の通りです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import sys
import numpy as np
import cv2

# 画像の読み込み
im = cv2.imread('numbers.png')
# グレイスケールに変換しぼかした上で二値化する
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 0)
thresh = cv2.adaptiveThreshold(blur, 255, 1, 1, 11, 2)

# 輪郭を抽出
contours = cv2.findContours(
thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[1]

# 抽出した領域を繰り返し処理する
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
if h < 20: # 小さすぎるのは飛ばす
continue
red = (0, 0, 255)
cv2.rectangle(im, (x, y), (x+w, y+h), red, 2)

cv2.imwrite('result1.png', im)

結果1(result1.png)
問題なく見つけた数字を赤枠で囲むことができています。

次に100個の数字が表示されている画像を入力にしてみます。
6行目の入力ファイル名を変更するだけです。
100個の数字(numbers100.png)

結果2
結果は上記のようになり数字の中まで赤枠で囲ってしまっています。
これを改善するためには、cv2.RETR_LISTというパラメータをcv2.RETR_EXTERNAL(15行目)に変更します。
このパラメータは領域の一番外側だけを検出するという意味になります。
下記に修正したソースの全体を記載しておきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import sys
import numpy as np
import cv2

# 画像の読み込み
im = cv2.imread('numbers100.png')
# グレイスケールに変換しぼかした上で二値化する
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 0)
thresh = cv2.adaptiveThreshold(blur, 255, 1, 1, 11, 2)

# 輪郭を抽出
contours = cv2.findContours(
thresh,
cv2.RETR_EXTERNAL, # ☆☆☆領域の一番外側だけを検出☆☆☆
cv2.CHAIN_APPROX_SIMPLE)[1]

# 抽出した領域を繰り返し処理する
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
if h < 20: # 小さすぎるのは飛ばす
continue
cv2.rectangle(im, (x, y), (x+w, y+h), (0,0,255), 2)
cv2.imwrite('result3.png', im)

結果3(result3.png)

今度は適切に全ての数字を認識することができました。

(Google Colaboratoryで動作確認しています。)