Contents - 目次(もくじ)
【動画】日本語 – 手書き編:文字検出の基礎
【日本語 – 手書き編 #3-1】
文字検出:横書き・縦書き(平仮名・片仮名・漢字・ローマ字・点・丸)
– Japanese handwritten character OCR:Text Detection
視聴時間:9分42秒
【動画の内容】
ステップ3-1:OCRプログラミングのための文字検出(日本語手書き)
0:00 事前準備
1:06 文字検出画像アップロード
2:08 文字検出
6:33 各種設定
7:06 文字検出画像の保存
9:13 次のステップへ
日本語用のOCRを作成するために、水平・垂直な文章の文字検出をしてみましょう。
既存のOCRプログラムで日本語の手書き文字の画像認識をしてみても、思ったよりも上手く行かない経験をされる方もいるのではないかと思います。
一連のプログラムが、これから、日本語をはじめとしたOCRに挑戦したい方の参考になることがありましたら幸いです。
Japanese handwritten character OCR Edition
:Text Detection.(Basic)
Introduction to Continuous Japanese handwritten character Image Recognition Python Programming.
日本語手書き文字画像・サンプルコードリンク:日本語の連続文字画像認識プログラム用
今回のプログラムでは、以下のサンプル画像
日本語手書き文字検出・OCR用サンプル画像
ダウンロード:日本語手書き文字検出・OCR用サンプル画像
日本語手書き文字検出やOCR用のサンプル画像です。
画像には、「ひらがな」「カタカナ」「漢字」「ローマ字」「、点」「。丸」が含まれます。
内の
・「H1_tegaki.png」(横書きの日本語文字画像)
・「V1_tegaki.png」(縦書きの日本語文字画像)
の画像を使います。
すぐに使えるGoogle Colaboratoryサンプルコードリンク
:Japanese-handwritten-text-detection-horizontal-vertical.ipynb | Google Colaboratory
(ファイル – ドライブにコピーを保存後にコピー環境で実行。プログラムを動かすにはGoogleアカウントでログインする必要があります)
【プログラムのライセンス】
The MIT License
Copyright 2021 child programmer
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
【水平・垂直の文章の日本語検出用:Pythonサンプルコード】
シンプルな横書き・縦書き文章の日本語手書き文字検出(ブロック→行と列→個別文字)
*ブロック:文字領域矩形の推定
バージョン情報(Python・各種ライブラリ)
python 3.7.10
imutils 0.5.4
natsort 5.5.0
opencv(opencv-python)4.1.2
matplotlib 3.2.2
ローカル環境で、指定したバージョンのインストールが難しい場合、最新のバージョンを入れてみてください。
① Googleアカウントでログイン
Googleアカウントでログインした状態で、「ファイル – ドライブにメニューを保存」し、以下の手順を進めます。
② 文字検出させたい画像をアップロードします。
今回のプログラムで使用する画像:
・水平/垂直に近い文字列の画像(横書き・縦書き)
・線なし
・汚れ/ノイズなし
③ 文字検出プログラムの実行。
以下の画像
・「H1_tegaki.png」(横書きの日本語文字画像)
・「V1_tegaki.png」(縦書きの日本語文字画像)
の文字検出を実行します。プログラムを実行前に「H1_tegaki.png」「V1_tegaki.png」をアップロードしておきます。
デフォルト(初期設定)では
・「H1_tegaki.png」(横書きの日本語文字画像)の画像
を検出する設定にしてあります。
#### 1.各種インポート ####
import os
import cv2
import numpy as np
from imutils import contours
import matplotlib.pyplot as plt
import glob
from natsort import natsorted # ファイルを自然順(0,1,2,3,・・,20,21,・・30,31,・・)に並べるために「natsort」を利用
#### 2.文字検出する画像 ####
input_file = 'H1_tegaki.png' # ここを変更
# 文字領域検出したい画像のファイル名を入力します
#### 3.各種設定 ####
# 横書き・縦書きの設定
# 横書きの文字領域検出・縦書きの文字領域検出の選択
horizontal = 0
vertical = 1
OCR_mode = 0 # ここを変更
# 横書きは「horizontal」または「0」・縦書きは「vertical」または「1」を入力
# 膨張処理の設定
#【横書き】大まかな文字領域の検出(ブロック検出)のための膨張処理(カーネルサイズ・膨張処理回数)の設定
block_horizontal_kernel_hight = 5 # カーネルの縦の高さ
block_horizontal_kernel_width = 5 # カーネルの横の幅
block_horizontal_iterations = 5 # 膨張処理回数
#【縦書き】大まかな文字領域の検出(ブロック検出)のための膨張処理(カーネルサイズ・膨張処理回数)の設定
block_vertical_kernel_hight = 5 # カーネルの縦の高さ
block_vertical_kernel_width = 5 # カーネルの横の幅
block_vertical_iterations = 9 # 膨張処理回数
#【横書き】行領域の検出(行検出)のための膨張処理(カーネルサイズ・膨張処理回数)の設定
column_horizontal_kernel_hight = 2 # カーネルの縦の高さ
column_horizontal_kernel_width = 5 # カーネルの横の幅
column_horizontal_iterations = 6 # 膨張処理回数
#【縦書き】列領域の検出(列検出)のための膨張処理(カーネルサイズ・膨張処理回数)の設定
row_vertical_kernel_hight = 5 # カーネルの縦の高さ
row_vertical_kernel_width = 3 # カーネルの横の幅
row_vertical_iterations = 6 # 膨張処理回数
#【横書き】個別の文字の検出(文字検出)のための膨張処理(カーネルサイズ・膨張処理回数)の設定
character_horizontal_kernel_hight = 5 # カーネルの縦の高さ 5
character_horizontal_kernel_width = 3 # カーネルの横の幅 3
character_horizontal_iterations = 2 # 膨張処理回数
#【縦書き】個別の文字の検出(文字検出)のための膨張処理(カーネルサイズ・膨張処理回数)の設定
character_vertical_kernel_hight = 3 # カーネルの縦の高さ 3
character_vertical_kernel_width = 5 # カーネルの横の幅 5
character_vertical_iterations = 2 # 膨張処理回数
# 輪郭のカット設定
# ブロック検出:文字領域検出した輪郭の「横幅」が、以下の範囲なら輪郭を残す
block_horizontal_height_minimum = 5 # 最小値(ピクセル)
block_horizontal_height_max = 1000 # 最大値(ピクセル)
# ブロック検出:文字領域検出した輪郭の「縦の高さ」が、以下の範囲なら輪郭を残す
block_vertical_height_minimum = 5 # 最小値(ピクセル)
block_vertical_height_max = 1000 # 最大値(ピクセル)
# 行検出:文字領域検出した輪郭の「横幅」が、以下の範囲なら輪郭を残す
row_column_horizontal_height_minimum = 5 # 最小値(ピクセル)
row_column_horizontal_height_max = 1000 # 最大値(ピクセル)
# 列検出:文字領域検出した輪郭の「縦の高さ」が、以下の範囲なら輪郭を残す
row_column_vertical_height_minimum = 5 # 最小値(ピクセル)
row_column_vertical_height_max = 1000 # 最大値(ピクセル)
# 個別の文字領域検出した輪郭の「横幅」が、以下の範囲なら輪郭を残す
character_text_detection_horizontal_height_minimum = 5 # 最小値(ピクセル)
character_text_detection_horizontal_height_max = 300 # 最大値(ピクセル)
# 個別の文字領域検出した輪郭の「縦の高さ」が、以下の範囲なら輪郭を残す
character_text_detection_vertical_height_minimum = 10 # 最小値(ピクセル)
character_text_detection_vertical_height_max = 300 # 最大値(ピクセル)
#### 4.大まかな文字領域の検出(ブロック検出) ####
# 画像から、ブロック検出をおこないます
# 「block_ROI_img〜.png」(ブロック検出画像)を作成します
# 「block_text-detection.png」(ブロック検出の結果を、元の画像に描画した画像)を作成します
# 画像から文字領域を検出・抽出する処理
def block_contours (OCR_input_file):
# 画像の読み込み
img = cv2.imread(OCR_input_file)
# モノクロ・グレースケール画像へ変換(2値化前の画像処理)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 2値化(Binarization):白(1)黒(0)のシンプルな2値画像に変換
retval, img_binary = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 白部分の膨張処理(Dilation):モルフォロジー変換 - 2値画像を対象
if OCR_mode == 0: # 横書きの場合
kernel = np.ones((block_horizontal_kernel_hight, block_horizontal_kernel_width),np.uint8) # カーネル(構造的要素):全要素の値が1の縦横が任意のピクセルのカーネル
img_dilation = cv2.dilate(img_binary,kernel,iterations = block_horizontal_iterations) #「iterations=」繰り返し膨張処理を行う回数を指定
elif OCR_mode == 1: # 縦書きの場合
kernel = np.ones((block_vertical_kernel_hight, block_vertical_kernel_width),np.uint8) # カーネル(構造的要素):全要素の値が1の縦横が任意のピクセルのカーネル
img_dilation = cv2.dilate(img_binary,kernel,iterations = block_vertical_iterations) #「iterations=」繰り返し膨張処理を行う回数を指定
# 解説用のコメント(2値化)
print('\n【2値化処理画像 - Binarization】')
print(' 画像の2値化と白部分の膨張を工夫することで、大まかな文字領域の検出(ブロック検出)をしています。')
print(' この段階で、文字が「白」として処理できていないと輪郭の検出がしにくいようでした。')
# 膨張処理後の2値化画像の表示
plt.imshow(cv2.cvtColor(img_dilation, cv2.COLOR_BGR2RGB))
plt.show()
# 輪郭の検出
#「findContours」の返り値「cnts(contours)」は輪郭毎の座標組・「hierarchy」は輪郭の階層構造
#「cv2.RETR_EXTERNAL」:最も外側の輪郭を返す
#「cv2.CHAIN_APPROX_SIMPLE」:輪郭上の全点の情報を保持しない。輪郭の情報を圧縮
cnts, hierarchy = cv2.findContours(img_dilation, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if OCR_mode == 0: # 横書きの場合
cnts, hierarchy = contours.sort_contours(cnts, method='top-to-bottom') # 上から下に並び替え
elif OCR_mode == 1: # 縦書きの場合
cnts, hierarchy = contours.sort_contours(cnts, method='right-to-left') # 右から左に並び替え
# ROI(Region of Interest:興味領域・対象領域)抽出用の初期設定
block_ROI_index = 0
# 抽出した輪郭を「x, y, w(横の幅), h(縦の高さ)」の単純なリストに変換
result = []
for contour in cnts:
x, y, w, h = cv2.boundingRect(contour) # 外接矩形の左上の位置は(x,y),横の幅と縦の高さは(w,h)
# 大きすぎる小さすぎる領域を除去。処理する画像サイズに合わせて微調整が必要
if not block_vertical_height_minimum < w < block_vertical_height_max:
continue
if not block_horizontal_height_minimum < h < block_horizontal_height_max: #輪郭の描画は画像サイズを超えることもあるようでした。
continue
# ROI抽出:画像の切り抜きと保存
block_ROI = img[y:y+h, x:x+w]
cv2.imwrite('block_ROI_img{}.png'.format(block_ROI_index), block_ROI)
block_ROI_index += 1
#resultに要素を追加
result.append([x, y, w, h])
# 画面に矩形の輪郭を描画 (描画機能)
for x, y, w, h in result:
cv2.rectangle(img, (x, y), (x+w, y+h), (100, 255, 100), 3) # 色の指定はRGB(100, 255, 100)。「3」は 太さ。数字を大きくすると太い輪郭が描画される。
# 解説用のコメント(文字領域の輪郭検出・抽出)
if OCR_mode == 0: # 横書きの場合
print('\n【横書きの文字領域の輪郭検出・抽出結果 - Text detection・Contours】')
elif OCR_mode == 1: #縦書きの場合
print('\n【縦書きの文字領域の輪郭検出・抽出結果 - Text detection・Contours】')
print(' 枠が大きすぎる場合・小さすぎる場合には輪郭を除去しています。画像によって微調整する必要があります。')
# 文字領域の輪郭検出・抽出結果の表示
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.savefig('block_text-detection.png', dpi=300) # 抽出結果の画像ファイルも保存しています。「dpi」は何も指定しないと dpi=72
plt.show()
## 大まかな文字領域の検出(ブロック検出)の関数(block_contours)の実行
block_contours (input_file)
#### 5.横書き・縦書きの行・列領域の検出(行と列の検出) ####
# 画像から行と列の検出をします
# 「row_column_ROI_img〜.png」(行や列を検出した画像)を作成します
def text_row_column_detection (OCR_input_file):
# 画像の読み込み
row_column_img = cv2.imread(OCR_input_file)
# モノクロ・グレースケール画像へ変換(2値化前の画像処理)
row_column_img_gray = cv2.cvtColor(row_column_img, cv2.COLOR_BGR2GRAY)
# 2値化(Binarization):白(1)黒(0)のシンプルな2値画像に変換
retval, row_column_img_binary = cv2.threshold(row_column_img_gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 白部分の膨張処理(Dilation):モルフォロジー変換 - 2値画像を対象
if OCR_mode == 0: # 横書きの場合
kernel = np.ones((column_horizontal_kernel_hight, column_horizontal_kernel_width),np.uint8) # カーネル(構造的要素):全要素の値が1の縦横が任意のピクセルのカーネル
row_column_img_dilation = cv2.dilate(row_column_img_binary,kernel,iterations = column_horizontal_iterations) #「iterations=」繰り返し膨張処理を行う回数を指定
print('\n【各行の2値化処理画像 - Binarization】')
elif OCR_mode == 1: #縦書きの場合
kernel = np.ones((row_vertical_kernel_hight, row_vertical_kernel_width),np.uint8) # カーネル(構造的要素):全要素の値が1の縦横が任意のピクセルのカーネル
row_column_img_dilation = cv2.dilate(row_column_img_binary,kernel,iterations = row_vertical_iterations) #「iterations=」繰り返し膨張処理を行う回数を指定
print('\n【各列の2値化処理画像 - Binarization】')
print(' 画像の2値化と白部分の膨張を工夫することで、文字と文字の繋がりを検出しています。')
print(' 画像のテキストの文字の「太さ」「行間」「文字間隔」によっては、画像のリサイズの微調整や膨張処理の微調整が必要です。')
# 膨張処理後の2値化画像の表示
plt.imshow(cv2.cvtColor(row_column_img_dilation, cv2.COLOR_BGR2RGB))
plt.show()
# 輪郭の検出
#「findContours」の返り値「contours」は輪郭毎の座標組・「hierarchy」は輪郭の階層構造
#「cv2.RETR_EXTERNAL」:最も外側の輪郭を返す
#「cv2.CHAIN_APPROX_SIMPLE」:輪郭上の全点の情報を保持しない。輪郭の情報を圧縮
cnts, hierarchy = cv2.findContours(row_column_img_dilation, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if OCR_mode == 0: # 横書きの場合
cnts, hierarchy = contours.sort_contours(cnts, method='top-to-bottom') # 上から下に並び替え
elif OCR_mode == 1: # 縦書きの場合
cnts, hierarchy = contours.sort_contours(cnts, method='right-to-left') # 右から左に並び替え
# 抽出した輪郭を「x, y, w(横の幅), h(縦の高さ)」の単純なリストに変換
row_column_result = []
for row_column_contour in cnts:
x, y, w, h = cv2.boundingRect(row_column_contour) # 外接矩形の左上の位置は(x,y),横の幅と縦の高さは(w,h)
# 大きすぎる小さすぎる領域を除去。処理する画像サイズに合わせて微調整が必要
if not row_column_vertical_height_minimum < w < row_column_vertical_height_max:
continue
if not row_column_horizontal_height_minimum < h < row_column_horizontal_height_max: #輪郭の描画は画像サイズを超えることもあるようでした。
continue
# resultに要素を追加
row_column_result.append([x, y, w, h])
# 画面に矩形の輪郭を描画 (描画機能:for〜cv2.rectangleまでの2行をコメントアウト、または削除すると輪郭の描画を無効にできます)
for x, y, w, h in row_column_result:
cv2.rectangle(row_column_img, (x, y), (x+w, y+h), (100, 255, 100), 2) # 色の指定はRGB(100, 255, 100)。「2」は 太さ。数字を大きくすると太い輪郭が描画される。
# 解説用のコメント(輪郭検出・抽出)
if OCR_mode == 0: # 横書きの場合
print('\n【各行の文字の輪郭検出・抽出結果 - Text recognition・Contours】')
elif OCR_mode == 1: #縦書きの場合
print('\n【各列の文字の輪郭検出・抽出結果 - Text recognition・Contours】')
print(' 枠が大きすぎる場合・小さすぎる場合には輪郭を除去しています。画像によって微調整する必要があります。')
# 輪郭検出・抽出結果の表示
plt.imshow(cv2.cvtColor(row_column_img, cv2.COLOR_BGR2RGB))
plt.show()
##「row_column_ROI_img〜.png」の画像を保存するための処理
#「block_ROI_img〜.png」という名前のファイルの取得
file_list = glob.glob('block_ROI_img*png')
#「block_ROI_img〜.png」(〜の部分に0や1などの数字が入る)という名前のファイルを0から順番に並び替え
print(natsorted(file_list))
# ROI(Region of Interest:興味領域・対象領域)抽出用の初期設定
row_column_ROI_index = 0
#「block_ROI_img〜.png」のファイルを順番に処理する
for file in natsorted(file_list):
row_column_img = cv2.imread(file)
# モノクロ・グレースケール画像へ変換(2値化前の画像処理)
row_column_img_gray = cv2.cvtColor(row_column_img, cv2.COLOR_BGR2GRAY)
# 2値化(Binarization):白(1)黒(0)のシンプルな2値画像に変換
retval, row_column_img_binary = cv2.threshold(row_column_img_gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 白部分の膨張処理(Dilation):モルフォロジー変換 - 2値画像を対象
if OCR_mode == 0: # 横書きの場合
kernel = np.ones((column_horizontal_kernel_hight, column_horizontal_kernel_width),np.uint8) # カーネル(構造的要素):全要素の値が1の縦横が任意のピクセルのカーネル
row_column_img_dilation = cv2.dilate(row_column_img_binary,kernel,iterations = column_horizontal_iterations) #「iterations=」繰り返し膨張処理を行う回数を指定
#print('\n【各行の2値化処理画像 - Binarization】')
elif OCR_mode == 1: #縦書きの場合
kernel = np.ones((row_vertical_kernel_hight, row_vertical_kernel_width),np.uint8) # カーネル(構造的要素):全要素の値が1の縦横が任意のピクセルのカーネル
row_column_img_dilation = cv2.dilate(row_column_img_binary,kernel,iterations = row_vertical_iterations) #「iterations=」繰り返し膨張処理を行う回数を指定
cnts, hierarchy = cv2.findContours(row_column_img_dilation, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if OCR_mode == 0: # 横書きの場合
cnts, hierarchy = contours.sort_contours(cnts, method='top-to-bottom') # 上から下に並び替え
elif OCR_mode == 1: # 縦書きの場合
cnts, hierarchy = contours.sort_contours(cnts, method='right-to-left') # 右から左に並び替え
# 抽出した輪郭を「x, y, w(横の幅), h(縦の高さ)」の単純なリストに変換
for row_column_contour in cnts:
x, y, w, h = cv2.boundingRect(row_column_contour) # 外接矩形の左上の位置は(x,y),横の幅と縦の高さは(w,h)
# ROI抽出:画像の切り抜きと保存。 ここで最後の処理だけの行と列の画像ファイルが残るのみなのでできない
row_column_ROI = row_column_img[y:y+h, x:x+w]
cv2.imwrite('row_column_ROI_img{}.png'.format(row_column_ROI_index), row_column_ROI)
row_column_ROI_index += 1
#### 6.個別の文字の検出(文字検出)+ 個別の文字検出枠の描画設定 ####
# 「row_column_ROI_img〜.png」(行や列を検出した画像)から、行や列ごとに個別の文字を検出します
# 「OCR_img〜.png」(個別文字検出画像)を作成します
# 「個別の文字検出枠の描画設定:画面に矩形の輪郭を描画」のコードをコメントアウトまたは削除すると個別の文字検出枠を無効化できます
# 個別の文字検出枠の有無や太さによってOCR結果の精度に影響がでます。基本的に枠線が無い方が精度は良いです
# 個別の文字検出枠があると、どの程度文字検出ができているか把握しやすいので、お好みに応じて調整してみてください
def find_draw_contours (OCR_input_file):
# 画像の読み込み
predict_img = cv2.imread(OCR_input_file)
# モノクロ・グレースケール画像へ変換(2値化前の画像処理)
predict_img_gray = cv2.cvtColor(predict_img, cv2.COLOR_BGR2GRAY)
# 2値化(Binarization):白(1)黒(0)のシンプルな2値画像に変換
retval, predict_img_binary = cv2.threshold(predict_img_gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 白部分の膨張処理(Dilation):モルフォロジー変換 - 2値画像を対象
if OCR_mode == 0: # 横書きの場合
kernel = np.ones((character_horizontal_kernel_hight, character_horizontal_kernel_width),np.uint8) # カーネル(構造的要素):全要素の値が1の縦横が任意のピクセルのカーネル
predict_img_dilation = cv2.dilate(predict_img_binary,kernel,iterations = character_horizontal_iterations) #「iterations=」繰り返し膨張処理を行う回数を指定
elif OCR_mode == 1: #縦書きの場合
kernel = np.ones((character_vertical_kernel_hight, character_vertical_kernel_width),np.uint8) # カーネル(構造的要素):全要素の値が1の縦横が任意のピクセルのカーネル
predict_img_dilation = cv2.dilate(predict_img_binary,kernel,iterations = character_vertical_iterations) #「iterations=」繰り返し膨張処理を行う回数を指定
# 解説用のコメント(2値化)
if OCR_mode == 0: # 横書きの場合
print('\n【各行の2値化処理画像 - Binarization】')
elif OCR_mode == 1: #縦書きの場合
print('\n【各列の2値化処理画像 - Binarization】')
print(' 画像の2値化と白部分の膨張を工夫することで、個別の文字を検出しやすいようにしています。')
print(' 画像のテキストの文字の「太さ」「行間」「文字間隔」によっては、画像のリサイズや膨張処理の微調整が必要です。')
print(' この段階で、文字が他の文字と繋がってしまうと、個別の文字の検出ができなくなります。')
# 膨張処理後の2値化画像の表示
plt.imshow(cv2.cvtColor(predict_img_dilation, cv2.COLOR_BGR2RGB))
plt.show()
# 輪郭の検出
#「findContours」の返り値「contours」は輪郭毎の座標組・「hierarchy」は輪郭の階層構造
#「cv2.RETR_EXTERNAL」:最も外側の輪郭を返す
#「cv2.CHAIN_APPROX_SIMPLE」:輪郭上の全点の情報を保持しない。輪郭の情報を圧縮
cnts, hierarchy = cv2.findContours(predict_img_dilation, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if OCR_mode == 0: # 横書きの場合
cnts, hierarchy = contours.sort_contours(cnts, method='left-to-right') # 左から右に並び替え
elif OCR_mode == 1: # 縦書きの場合
cnts, hierarchy = contours.sort_contours(cnts, method='top-to-bottom') # 上から下に並び替え
# ROI(Region of Interest:興味領域・対象領域)抽出用の初期設定
OCR_index = 0
# 抽出した輪郭を「x, y, w(横の幅), h(縦の高さ)」の単純なリストに変換
predict_result = []
for predict_contour in cnts:
x, y, w, h = cv2.boundingRect(predict_contour) # 外接矩形の左上の位置は(x,y),横の幅と縦の高さは(w,h)
# 大きすぎる小さすぎる領域を除去。処理する画像サイズに合わせて微調整が必要
if not character_text_detection_horizontal_height_minimum < w < character_text_detection_horizontal_height_max:
continue
if not character_text_detection_vertical_height_minimum < h < character_text_detection_vertical_height_max: #輪郭の描画は画像サイズを超えることもあるようでした。
continue
# ROI抽出:画像の切り抜きと保存。
predict_ROI = predict_img[y:y+h, x:x+w]
cv2.imwrite('OCR_img{}.png'.format(OCR_index), predict_ROI)
OCR_index += 1
#resultに要素を追加
predict_result.append([x, y, w, h])
## 個別の文字検出枠の描画設定:画面に矩形の輪郭を描画
# 描画機能:for〜cv2.rectangleまでの2行をコメントアウト、または削除すると輪郭の描画を無効にできます
# 実際に文字認識・OCRさせる場合には、ここの輪郭が無い方が精度が上がりやすいです
for x, y, w, h in predict_result:
cv2.rectangle(predict_img, (x, y), (x+w, y+h), (100, 255, 100), 2) # 色の指定はRGB(100, 255, 100)。「2」は 太さ。数字を大きくすると太い輪郭が描画される。
# 解説用のコメント(輪郭検出・抽出)
if OCR_mode == 0: # 横書きの場合
print('\n【各行の文字の輪郭検出・抽出結果 - Text recognition・Contours】')
elif OCR_mode == 1: #縦書きの場合
print('\n【各列の文字の輪郭検出・抽出結果 - Text recognition・Contours】')
print(' 枠が大きすぎる場合・小さすぎる場合には輪郭を除去しています。画像によって微調整する必要があります。')
# 輪郭検出・抽出結果の表示
plt.imshow(cv2.cvtColor(predict_img, cv2.COLOR_BGR2RGB))
plt.show()
##「OCR_img〜.png」の画像を保存するための処理
#「row_column_ROI_img〜.png」という名前のファイルの取得
file_list = glob.glob('row_column_ROI_img*png')
#「row_column_ROI_img〜.png」(〜の部分に0や1などの数字が入る)という名前のファイルを0から順番に並び替え
print(natsorted(file_list))
# ROI(Region of Interest:興味領域・対象領域)抽出用の初期設定
OCR_index = 0
#「row_column_ROI_img〜.png」のファイルを順番に処理する
for file in natsorted(file_list):
predict_img = cv2.imread(file)
# モノクロ・グレースケール画像へ変換(2値化前の画像処理)
predict_img_gray = cv2.cvtColor(predict_img, cv2.COLOR_BGR2GRAY)
# 2値化(Binarization):白(1)黒(0)のシンプルな2値画像に変換
retval, predict_img_binary = cv2.threshold(predict_img_gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 白部分の膨張処理(Dilation):モルフォロジー変換 - 2値画像を対象
if OCR_mode == 0: # 横書きの場合
kernel = np.ones((character_horizontal_kernel_hight, character_horizontal_kernel_width),np.uint8) # カーネル(構造的要素):全要素の値が1の縦横が任意のピクセルのカーネル
predict_img_dilation = cv2.dilate(predict_img_binary,kernel,iterations = character_horizontal_iterations) #「iterations=」繰り返し膨張処理を行う回数を指定
elif OCR_mode == 1: #縦書きの場合
kernel = np.ones((character_vertical_kernel_hight, character_vertical_kernel_width),np.uint8) # カーネル(構造的要素):全要素の値が1の縦横が任意のピクセルのカーネル
predict_img_dilation = cv2.dilate(predict_img_binary,kernel,iterations = character_vertical_iterations) #「iterations=」繰り返し膨張処理を行う回数を指定
cnts, hierarchy = cv2.findContours(predict_img_dilation, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if OCR_mode == 0: # 横書きの場合
cnts, hierarchy = contours.sort_contours(cnts, method='left-to-right') # 左から右に並び替え
elif OCR_mode == 1: # 縦書きの場合
cnts, hierarchy = contours.sort_contours(cnts, method='top-to-bottom') # 上から下に並び替え
# 抽出した輪郭を「x, y, w(横の幅), h(縦の高さ)」の単純なリストに変換
predict_result = []
for predict_contour in cnts:
x, y, w, h = cv2.boundingRect(predict_contour) # 外接矩形の左上の位置は(x,y),横の幅と縦の高さは(w,h)
# 大きすぎる小さすぎる領域を除去。処理する画像サイズに合わせて微調整が必要
if not character_text_detection_horizontal_height_minimum < w < character_text_detection_horizontal_height_max:
continue
if not character_text_detection_vertical_height_minimum < h < character_text_detection_vertical_height_max: #輪郭の描画は画像サイズを超えることもあるようでした。
continue
# ROI抽出:画像の切り抜きと保存。→ 再学習用に別プログラムを作成する OCR_index自体は、文字数判断のために必要かもしれない。
predict_ROI = predict_img[y:y+h, x:x+w]
cv2.imwrite('OCR_img{}.png'.format(OCR_index), predict_ROI)
OCR_index += 1
#### 7.関数の実行等 ####
## 横書き・縦書きの行・列領域の検出(行と列の検出)する関数(text_row_column_detection)の実行
#「block_ROI_img〜.png」という名前のファイルの取得
file_list = glob.glob('block_ROI_img*png')
#「block_ROI_img〜.png」(〜の部分に0や1などの数字が入る)という名前のファイルを0から順番に並び替え
print(natsorted(file_list))
#「block_ROI_img〜.png」のファイルを順番に、
# 横書き・縦書きの行・列領域の検出(行と列の検出)する関数(text_row_column_detection)に入れる
for file in natsorted(file_list):
text_row_column_detection(file)
## 個別の文字の検出(文字検出)する関数(find_draw_contours)の実行
#「row_column_ROI_img〜.png」という名前のファイルの取得
file_list = glob.glob('row_column_ROI_img*png')
#「row_column_ROI_img〜.png」(〜の部分に0や1などの数字が入る)という名前のファイルを0から順番に並び替え
print(natsorted(file_list))
#「row_column_ROI_img〜.png」のファイルを順番に、
# 個別の文字の検出(文字検出)する関数(find_draw_contours)に入れる
for file in natsorted(file_list):
find_draw_contours(file)
print('\n\n* 上手く検出できない場合は「3.各種設定」の所で、「膨張処理の設定」「輪郭のカット設定」を変更してみてください。')
#### 8.出力した画像ファイルの削除 ####
# 今回のコードでは、画像を一度作成後に、プログラム内で利用し、最後に作成した画像を削除しています
# * 例外として「block_text-detection.png」(ブロック検出の結果を、元の画像に描画した画像)は、削除していません
# そのため、次に文字検出を実行する画像内の文字検出数などが少ないと、誤作動が起きるリスクがあります
# 画像ファイルを保存した場合には、一度生成された画像を削除してから、新たな画像の文字検出を試みてください
# 「個別の文字領域検出した画像(OCR_img*png)の削除」のコードをコメントアウトまたは、削除すると個別の文字検出画像を保存できます
# 大まかな文字領域の検出(ブロック検出)した画像(「block_ROI_img〜.png」)の削除:保存したい場合は、
# ここをコメントアウト(または削除)すると画像を保存できます
file_list = glob.glob("block_ROI_img*png")
for file in natsorted(file_list):
os.remove(file)
# 横書き・縦書きの行・列領域の検出(行と列の検出)した画像(「row_column_ROI_img〜.png」)の削除:保存したい場合は、
# ここをコメントアウト(または削除)すると画像を保存できます
file_list = glob.glob("row_column_ROI_img*png")
for file in natsorted(file_list):
os.remove(file)
# 個別の文字の検出(文字検出)画像(「OCR_img〜.png」)の削除:保存したい場合は、
# ここをコメントアウト(または削除)すると画像を保存できます(今回は一部の画像のみ。ステップ4のプログラムでは全てに対応。)
file_list = glob.glob('OCR_img*png')
for file in natsorted(file_list):
os.remove(file)
【出力結果例】(横書きの例)
出力結果例:【日本語 - 手書き編】連続文字検出:シンプルな横書き・縦書き(ひらがな・カタカナ・漢字・ローマ字・、点・。丸)
【横書きの文字領域の輪郭検出・抽出結果 - Text detection・Contours】
枠が大きすぎる場合・小さすぎる場合には輪郭を除去しています。画像によって微調整する必要があります。
【各行の文字の輪郭検出・抽出結果 - Text recognition・Contours】
枠が大きすぎる場合・小さすぎる場合には輪郭を除去しています。画像によって微調整する必要があります。
【次のステップ】
続いて、直線が混じっている画像の文字検出に挑戦
【ステップ3-2】
日本語認識:直線が描かれている画像で横書き・縦書き文字検出
直線除去の機能を実装:横書き・縦書き文章の日本語手書き文字検出(ブロック→行と列→個別文字)
:【日本語手書きOCR編】連続文字画像認識プログラミング入門(Python・OpenCV・Keras・CNN)| 一覧ページ