未分類」カテゴリーアーカイブ

数独の問題から1文字づつ切り出して認識を試みる

import cv2
from PIL import Image           # 画像処理ライブラリ
from matplotlib import pyplot as plt # データプロット用ライブラリ
import numpy as np              # データ分析用ライブラリ
import os                       # os の情報を扱うライブラリ
import pytesseract              # tesseract の python 用ライブラリ
import unicodedata
from pytesseract import Output

def remove_control_characters(s):
    return "".join(ch for ch in s if unicodedata.category(ch)[0]!="C")

# 以下の値は汎用性ナシ。
dx,dy=52,52
ox,oy=10,10
tD,tB=38,38
pz='100006800500000030000720000942003706703004100805009000476392581251087360098165420' 

img = cv2.imread("/home/mars/TEST50-2a.png")
#cv2.imshow('sudoku',img)
ok,ng=0,0
for x in range(9):
    for y in range(9):
        t=x*dx + ox
        b=y*dy+oy
        tt=t-2
        bb=b-2
        #cv2.rectangle(img,(t,b),(t+tD,b+tB),(0,0,255))
        sliced=img[tt:t+tD,bb:b+tB]
        #sliced=cv2.threshold(tmp, 150, 255, cv2.THRESH_BINARY)
        im_list = np.array(sliced)
        img_rgb = cv2.cvtColor(im_list, cv2.COLOR_BGR2RGB)
        pos=str(x)+','+str(y)
        #txt = pytesseract.image_to_string(im_list,lang="jpn",config='--psm 10 outputbase digits')
        txt=pytesseract.image_to_string(sliced, config='-l eng  --psm 6 outputbase digits')
        junk=remove_control_characters(txt)
        #print(x,y,len(junk))
        ans=pz[x*9+y]
        if  len(junk)<1:
            if ans=='0':
                ok=ok+1
                junk='O'
            else:
                junk='X'
                ng=ng+1
        else:
            if ans==junk:
                ok=ok+1
            else:
                junk='X'
                ng=ng+1

        out=out+junk+'('+ans+')'
        #cv2.imshow('slice:'+pos,sliced)
    print(str(x)+'--->',out,'  correct=',ok,'  in-correct=',ng)
    out=''
#cv2.imshow('slice',img)
print('Success rate:',round(100*ok/81,2),'[%]')

print('done')

正答率:86%

0---> 1(1)O(0)O(0)O(0)O(0)6(6)8(8)O(0)O(0)   correct= 9   in-correct= 0
1---> X(5)O(0)O(0)O(0)X(0)O(0)O(0)3(3)O(0)   correct= 16   in-correct= 2
2---> O(0)O(0)O(0)X(7)2(2)O(0)O(0)O(0)O(0)   correct= 24   in-correct= 3
3---> 9(9)4(4)2(2)O(0)O(0)3(3)X(7)O(0)6(6)   correct= 32   in-correct= 4
4---> 7(7)O(0)3(3)O(0)O(0)4(4)X(1)O(0)O(0)   correct= 40   in-correct= 5
5---> 8(8)X(0)5(5)O(0)X(0)9(9)O(0)O(0)O(0)   correct= 47   in-correct= 7
6---> 4(4)7(7)6(6)3(3)9(9)2(2)5(5)8(8)X(1)   correct= 55   in-correct= 8
7---> X(2)5(5)X(1)O(0)8(8)7(7)3(3)6(6)O(0)   correct= 62   in-correct= 10
8---> O(0)9(9)8(8)X(1)6(6)5(5)4(4)2(2)O(0)   correct= 70   in-correct= 11
Success rate: 86.42 [%]
done

オープンソースの文字認識tesseractを試してみる(2)。カメラで撮影した画像の場合

認識率がかなり低下することがわかる。カメラで撮影する場合は、極力品質の良い画像となるよう工夫が必要。(特に、傾き、コントラストが問題となりそう)

結果:

0123456789 |
前 量 旧 |
0123456789 :
623456789 :
。 本 日 は 晴天 ? 雨降り で す 。 日 本 語 の 文字 認識 テス ト 。

僅かに傾きがある画像

認識結果:

Start....
Done.
UL
345675 9 リリ
| 0123456789 |

oo | 上
II

オープンソースの文字認識tesseractを試してみる。

環境

$ uname -a
Linux ps2 5.10.63-v7l+ #1457 SMP Tue Sep 28 11:26:14 BST 2021 armv7l GNU/Linux

$ cat /etc/os-release
PRETTY_NAME="Raspbian GNU/Linux 10 (buster)"
NAME="Raspbian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=raspbian
ID_LIKE=debian

インストールの参考にしたサイト

「Raspberry Pi 3B+における、Tesseract(5.0.0 alpha)のインストール方法と基本操作」

$ sudo apt-get install tesseract-ocr-script-jpan
$ tesseract --version
tesseract 4.0.0
 leptonica-1.76.0
  libgif 5.1.4 : libjpeg 6b (libjpeg-turbo 1.5.2) : libpng 1.6.36 : libtiff 4.1.0 : zlib 1.2.11 : libwebp 0.6.1 : libopenjp2 2.3.0

$  tesseract --list-langs
List of available languages (3):
Japanese
eng
osd

日本語のトレーニングデータ取得とインストール(精度優先)
$ git clone https://github.com/tesseract-ocr/tessdata_best.git
$ sudo cp tessdata_best/jpn* /usr/share/tesseract-ocr/4.00/tessdata/

追加したトレーニングデータの確認
$  tesseract --list-langs
List of available languages (5):
Japanese
eng
jpn
jpn_vert
osd

ペイントプログラムで書いたTEST-JPN.pngファイを認識させてみる。

フォントサイズ(20/12)と書体(BOLD)の違いも確認。

 $ time tesseract TEST-JPN.png stdout -l jpn
0123456789
0123456789

0123456789

本 日 は 晴天 ? 雨降り で す 。 日 本 語 の 文字 認識 テス ト 。


real    1m4.283s
user    1m43.622s
sys     0m3.248s

この例では、なんと認識率100%。

罫線の中の数字をも試してみました。(TEST-JPN3.png)

表組にすると、まったく認識されない結果に!

 $ time tesseract TEST-JPN3.png stdout -l jpn
Empty page!!
Empty page!!


real    0m4.568s
user    0m2.977s
sys     0m1.382s

表を認識させる記事があったので、こちらも試してみたが、pytesseract.image_to_stringを呼び出した後で、戻ってこない印象。 戻ってきていますが、空文字列となって、認識に失敗しているようです。前述のコマンドラインで認識できた画像は、pythonのコードでも問題なく認識。

「『表の文字』と『欄外の文字』の認識(Python + Tesseract)」

ここから、jupyter notebookで pytesseract をインストール
!pip install pytesseract 

Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting pytesseract
  Downloading https://www.piwheels.org/simple/pytesseract/pytesseract-0.3.8-py2.py3-none-any.whl (14 kB)
Requirement already satisfied: Pillow in /home/mars/.pyenv/versions/3.7.3/lib/python3.7/site-packages (from pytesseract) (8.3.1)
Installing collected packages: pytesseract
Successfully installed pytesseract-0.3.8  

認識のテストコード

###############################################################################
# ライブラリインポート
###############################################################################
import os                       # os の情報を扱うライブラリ
import pytesseract              # tesseract の python 用ライブラリ
from PIL import Image           # 画像処理ライブラリ
import matplotlib.pyplot as plt # データプロット用ライブラリ
import numpy as np              # データ分析用ライブラリ

img=Image.open('/home/mars/TEST-JPN3.png')
# 画像を配列に変換
im_list = np.array(img)
 
# データプロットライブラリに貼り付け
plt.imshow(im_list)
 
# 表示
plt.show()
print('Start....') 
# テキスト抽出
txt = pytesseract.image_to_string(img,lang="jpn")
 
# 抽出したテキストの出力
print('Done.')
print(txt)
print()

枠線を消す処理を追加

import cv2
from PIL import Image           # 画像処理ライブラリ
from matplotlib import pyplot as plt # データプロット用ライブラリ
import numpy as np              # データ分析用ライブラリ
import os                       # os の情報を扱うライブラリ
import pytesseract              # tesseract の python 用ライブラリ

# 処理の対象
img = cv2.imread("/home/mars/TEST-JPN4.png")
img2 = img.copy()
img3 = img.copy()

# グレースケール
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray_list = np.array(gray)
 
# データプロットライブラリに貼り付け
#plt.imshow(gray_list)
#cv2.imwrite("calendar_mod.png", gray)

## 反転 ネガポジ変換
gray2 = cv2.bitwise_not(gray)
gray2_list = np.array(gray2)
#plt.imshow(gray2_list)
#cv2.imwrite("calendar_mod2.png", gray2)
lines = cv2.HoughLinesP(gray2, rho=1, theta=np.pi/360, threshold=80, minLineLength=80, maxLineGap=5)

for line in lines:
    x1, y1, x2, y2 = line[0]

    # 赤線を引く
    red_lines_img = cv2.line(img2, (x1,y1), (x2,y2), (0,0,255), 3)
    red_lines_np=np.array( red_lines_img)
    #cv2.imwrite("calendar_mod3.png", red_lines_img)

    # 線を消す(白で線を引く)
    no_lines_img = cv2.line(img3, (x1,y1), (x2,y2), (255,255,255), 3)
    no_lines=np.array( no_lines_img)
    plt.imshow(no_lines)
    #plt.show()
    #cv2.imwrite("calendar_mod4.png", no_lines_img)

print('OCR start...')
#txt = pytesseract.image_to_string(img, lang="jpn",config='osd --psm 6')
txt = pytesseract.image_to_string(no_lines_img, lang="jpn",config='--psm 6')

plt.show()
#cv2.imwrite("/home/mars/line_erased.png",no_lines_img)
print('---------OCR:results-----')
print(txt)
print('---------OCR done.---------') 

線が消された画像

認識結果。問題なく認識。ただし、以上は理想的(?)にきれいな画像の場合。カメラで撮影した画像については、さらに検証や処理の追加が必要になるかも??

---------OCR:results-----
0123456789
0123456789

0123456789

本 日 は 晴天 ? 雨降り で す 。 日 本 語 の 文字 認識 テス ト 。

---------OCR done.-------

leonard(C/2021 A1)彗星

2月上旬から中旬、レナード彗星(C/2021 A1)が5等前後で見えると予想されている。未明から明け方の東の空に見える。

撮影:2021/11/29未明 1枚の露光8秒で5分間分をライブスタック。

5分毎の撮影で約3時間分の動画。彗星が左上から右下方向へ移動している。

jetson nanoでcv2(cuda有効化)

ソースからインストール

python3でcv2をimportすると crashしてコアーダンプ。

問題の解決:export OPENBLAS_CORETYPE=ARMV8

こちらを参照して解決。Jetson NanoのPython3環境でIllegal instruction (cpre dumped)

処理速度の比較測定:約3倍

$ sudo nvpmodel -m 0
$ sudo jetson_clocks
$ python3 opencv_cuda.py
CPU = 2.7655137538909913[msec]
GPU = 1.0501614570617677[msec]
1
$ python3 opencv_cuda.py
CPU = 2.7816075325012206[msec]
GPU = 0.9869620561599731[msec]
1

opencv_cuda.py

import sys
import time
import cv2

### VALUES
NUM_REPEAT = 10000

### Read source image
img_src = cv2.imread("resource/lena.jpg")
cv2.imshow('img_src', img_src)


### Run with CPU
time_start = time.time()
for i in range (NUM_REPEAT):
    img_dst = cv2.resize(img_src, (300, 300))
time_end = time.time()
print ("CPU = {0}".format((time_end - time_start) * 1000 / NUM_REPEAT) + "[msec]")
cv2.imshow('CPU', img_dst)


### Run with GPU
img_gpu_src = cv2.cuda_GpuMat() # Allocate device memory only once, as memory allocation seems to take time...
img_gpu_dst = cv2.cuda_GpuMat()
time_start = time.time()
for i in range (NUM_REPEAT):
    img_gpu_src.upload(img_src)
    img_gpu_dst = cv2.cuda.resize(img_gpu_src, (300, 300))
    img_dst = img_gpu_dst.download()
time_end = time.time()
print ("GPU = {0}".format((time_end - time_start) * 1000 / NUM_REPEAT) + "[msec]")
cv2.imshow('GPU', img_dst)


key = cv2.waitKey(0)
cv2.destroyAllWindows()

print(cv2.cuda.getCudaEnabledDeviceCount())

cudaで利用できる機能を表示してみる

import cv2
cv2.__version__
dir(cv2.cuda)
['ALPHA_ATOP',
 'ALPHA_ATOP_PREMUL',
 'ALPHA_IN',
 'ALPHA_IN_PREMUL',
 'ALPHA_OUT',
 'ALPHA_OUT_PREMUL',
 'ALPHA_OVER',
 'ALPHA_OVER_PREMUL',
 'ALPHA_PLUS',
 'ALPHA_PLUS_PREMUL',
 'ALPHA_PREMUL',
 'ALPHA_XOR',
 'ALPHA_XOR_PREMUL',
 'BroxOpticalFlow_create',
 'COLOR_BAYER_BG2BGR_MHT',
 'COLOR_BAYER_BG2GRAY_MHT',
 'COLOR_BAYER_BG2RGB_MHT',
 'COLOR_BAYER_GB2BGR_MHT',
 'COLOR_BAYER_GB2GRAY_MHT',
 'COLOR_BAYER_GB2RGB_MHT',
 'COLOR_BAYER_GR2BGR_MHT',
 'COLOR_BAYER_GR2GRAY_MHT',
 'COLOR_BAYER_GR2RGB_MHT',
 'COLOR_BAYER_RG2BGR_MHT',
 'COLOR_BAYER_RG2GRAY_MHT',
 'COLOR_BAYER_RG2RGB_MHT',
 'COLOR_BayerBG2BGR_MHT',
 'COLOR_BayerBG2GRAY_MHT',
 'COLOR_BayerBG2RGB_MHT',
 'COLOR_BayerGB2BGR_MHT',
 'COLOR_BayerGB2GRAY_MHT',
 'COLOR_BayerGB2RGB_MHT',
 'COLOR_BayerGR2BGR_MHT',
 'COLOR_BayerGR2GRAY_MHT',
 'COLOR_BayerGR2RGB_MHT',
 'COLOR_BayerRG2BGR_MHT',
 'COLOR_BayerRG2GRAY_MHT',
 'COLOR_BayerRG2RGB_MHT',
 'CascadeClassifier_create',
 'DEVICE_INFO_COMPUTE_MODE_DEFAULT',
 'DEVICE_INFO_COMPUTE_MODE_EXCLUSIVE',
 'DEVICE_INFO_COMPUTE_MODE_EXCLUSIVE_PROCESS',
 'DEVICE_INFO_COMPUTE_MODE_PROHIBITED',
 'DYNAMIC_PARALLELISM',
 'DensePyrLKOpticalFlow_create',
 'DescriptorMatcher_createBFMatcher',
 'DeviceInfo_ComputeModeDefault',
 'DeviceInfo_ComputeModeExclusive',
 'DeviceInfo_ComputeModeExclusiveProcess',
 'DeviceInfo_ComputeModeProhibited',
 'EVENT_BLOCKING_SYNC',
 'EVENT_DEFAULT',
 'EVENT_DISABLE_TIMING',
 'EVENT_INTERPROCESS',
 'Event_BLOCKING_SYNC',
 'Event_DEFAULT',
 'Event_DISABLE_TIMING',
 'Event_INTERPROCESS',
 'Event_elapsedTime',
 'FEATURE_SET_COMPUTE_10',
 'FEATURE_SET_COMPUTE_11',
 'FEATURE_SET_COMPUTE_12',
 'FEATURE_SET_COMPUTE_13',
 'FEATURE_SET_COMPUTE_20',
 'FEATURE_SET_COMPUTE_21',
 'FEATURE_SET_COMPUTE_30',
 'FEATURE_SET_COMPUTE_32',
 'FEATURE_SET_COMPUTE_35',
 'FEATURE_SET_COMPUTE_50',
 'FarnebackOpticalFlow_create',
 'FastFeatureDetector_create',
 'GLOBAL_ATOMICS',
 'GpuMat_defaultAllocator',
 'GpuMat_setDefaultAllocator',
 'HOG_create',
 'HOST_MEM_PAGE_LOCKED',
 'HOST_MEM_SHARED',
 'HOST_MEM_WRITE_COMBINED',
 'HostMem_PAGE_LOCKED',
 'HostMem_SHARED',
 'HostMem_WRITE_COMBINED',
 'NATIVE_DOUBLE',
 'NVIDIA_OPTICAL_FLOW_1_0_NV_OF_PERF_LEVEL_FAST',
 'NVIDIA_OPTICAL_FLOW_1_0_NV_OF_PERF_LEVEL_MAX',
 'NVIDIA_OPTICAL_FLOW_1_0_NV_OF_PERF_LEVEL_MEDIUM',
 'NVIDIA_OPTICAL_FLOW_1_0_NV_OF_PERF_LEVEL_SLOW',
 'NVIDIA_OPTICAL_FLOW_1_0_NV_OF_PERF_LEVEL_UNDEFINED',
 'NvidiaOpticalFlow_1_0_NV_OF_PERF_LEVEL_FAST',
 'NvidiaOpticalFlow_1_0_NV_OF_PERF_LEVEL_MAX',
 'NvidiaOpticalFlow_1_0_NV_OF_PERF_LEVEL_MEDIUM',
 'NvidiaOpticalFlow_1_0_NV_OF_PERF_LEVEL_SLOW',
 'NvidiaOpticalFlow_1_0_NV_OF_PERF_LEVEL_UNDEFINED',
 'NvidiaOpticalFlow_1_0_create',
 'ORB_create',
 'OpticalFlowDual_TVL1_create',
 'SHARED_ATOMICS',
 'SURF_CUDA_ANGLE_ROW',
 'SURF_CUDA_HESSIAN_ROW',
 'SURF_CUDA_LAPLACIAN_ROW',
 'SURF_CUDA_OCTAVE_ROW',
 'SURF_CUDA_ROWS_COUNT',
 'SURF_CUDA_SIZE_ROW',
 'SURF_CUDA_X_ROW',
 'SURF_CUDA_Y_ROW',
 'SparsePyrLKOpticalFlow_create',
 'StereoBeliefPropagation_estimateRecommendedParams',
 'StereoConstantSpaceBP_estimateRecommendedParams',
 'Stream_Null',
 'TargetArchs_has',
 'TargetArchs_hasBin',
 'TargetArchs_hasEqualOrGreater',
 'TargetArchs_hasEqualOrGreaterBin',
 'TargetArchs_hasEqualOrGreaterPtx',
 'TargetArchs_hasEqualOrLessPtx',
 'TargetArchs_hasPtx',
 'WARP_SHUFFLE_FUNCTIONS',
 '__doc__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 'abs',
 'absSum',
 'absdiff',
 'add',
 'addWeighted',
 'alphaComp',
 'bilateralFilter',
 'bitwise_and',
 'bitwise_not',
 'bitwise_or',
 'bitwise_xor',
 'blendLinear',
 'buildWarpAffineMaps',
 'buildWarpPerspectiveMaps',
 'calcAbsSum',
 'calcHist',
 'calcNorm',
 'calcNormDiff',
 'calcSqrSum',
 'calcSum',
 'cartToPolar',
 'compare',
 'copyMakeBorder',
 'countNonZero',
 'createBackgroundSubtractorMOG',
 'createBackgroundSubtractorMOG2',
 'createBoxFilter',
 'createBoxMaxFilter',
 'createBoxMinFilter',
 'createCLAHE',
 'createCannyEdgeDetector',
 'createColumnSumFilter',
 'createContinuous',
 'createConvolution',
 'createDFT',
 'createDerivFilter',
 'createDisparityBilateralFilter',
 'createGaussianFilter',
 'createGeneralizedHoughBallard',
 'createGeneralizedHoughGuil',
 'createGoodFeaturesToTrackDetector',
 'createHarrisCorner',
 'createHoughCirclesDetector',
 'createHoughLinesDetector',
 'createHoughSegmentDetector',
 'createLaplacianFilter',
 'createLinearFilter',
 'createLookUpTable',
 'createMedianFilter',
 'createMinEigenValCorner',
 'createMorphologyFilter',
 'createRowSumFilter',
 'createScharrFilter',
 'createSeparableLinearFilter',
 'createSobelFilter',
 'createStereoBM',
 'createStereoBeliefPropagation',
 'createStereoConstantSpaceBP',
 'createTemplateMatching',
 'cvtColor',
 'demosaicing',
 'dft',
 'divide',
 'drawColorDisp',
 'ensureSizeIsEnough',
 'equalizeHist',
 'evenLevels',
 'exp',
 'findMinMax',
 'findMinMaxLoc',
 'flip',
 'gammaCorrection',
 'gemm',
 'getCudaEnabledDeviceCount',
 'getDevice',
 'histEven',
 'histRange',
 'integral',
 'log',
 'magnitude',
 'magnitudeSqr',
 'max',
 'meanShiftFiltering',
 'meanShiftProc',
 'meanShiftSegmentation',
 'meanStdDev',
 'merge',
 'min',
 'minMax',
 'minMaxLoc',
 'mulAndScaleSpectrums',
 'mulSpectrums',
 'multiply',
 'norm',
 'normalize',
 'phase',
 'polarToCart',
 'pow',
 'printCudaDeviceInfo',
 'printShortCudaDeviceInfo',
 'pyrDown',
 'pyrUp',
 'rectStdDev',
 'reduce',
 'registerPageLocked',
 'remap',
 'reprojectImageTo3D',
 'resetDevice',
 'resize',
 'rotate',
 'setBufferPoolConfig',
 'setBufferPoolUsage',
 'setDevice',
 'split',
 'sqr',
 'sqrIntegral',
 'sqrSum',
 'sqrt',
 'subtract',
 'sum',
 'threshold',
 'transpose',
 'unregisterPageLocked',
 'warpAffine',
 'warpPerspective']