Python OpenCV数字識別判例を詳しく説明する。


前言
実践は真理を検証する唯一の規準である。
一筋縄でOpenCVを勉強するのは味気ないと思いますので、ネットでプロジェクトを案内する教程を探しました。話を多くしないで、手を下してやります。
一、判例紹介
クレジットカードの数字テンプレートを提供します。

要求:クレジットカードの数字を識別し、そのまま元の画像に印刷する。愚かに見えますが、数字を画像に印刷することができますので、数字を認識することができます。デジタルテキストに変換して保存することもできます。ナンバープレートの識別などの項目の考えはこのケースと似ています。
例:

原図

処理後の図
二、ステップ
大きく分けると次のような手順があります。
1.テンプレート読み込み
2.テンプレートの前処理、テンプレートの数字を分離して並べ替えます。
3.入力画像を前処理し、画像の数字部分を抽出する
4.数字をテンプレートの数字に合わせて、マッチング率が一番高いのは対応する数字です。
1、テンプレート読み込み、およびいくつかのパッケージの導入、関数定義など

import cv2 as cv
import numpy as np
import myutils
def cv_show(name, img):        #         
    cv.imshow(name, img)
    cv.waitKey(0)
#      
n = 'text'
img = cv.imread("images/ocr_a_reference.png")
# cv_show(n, template)        #         ,      
2、テンプレートの前処理、テンプレートの数字を分離して並べ替えます。

テンプレートの前処理手順:グレースケール、二値化、輪郭検出を行います。なお、openCVは輪郭を検出する際に白い枠を検出するので、テンプレート図の数字を二値化して白にする必要があります。

#         
ref = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# cv_show(n, ref)

#       ,         
ref = cv.threshold(ref, 10, 255, cv.THRESH_BINARY_INV)[1]  #    ,          ,         
cv_show(n, ref)

#          ,      
refCnts, hierarchy = cv.findContours(ref.copy(), cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE)
cv.drawContours(img, refCnts, -1, (0, 0, 255), 2)  #           
# cv_show(n, img)

赤い部分が検出された輪郭です。
次に輪郭の並べ替えを行います。検出された輪郭は無秩序であるため、輪郭の左上隅点のx座標に従って並べ替えられます。輪郭を並べた後、順番に辞書に入れると、辞書のキーのペアが正しく一致します。例えば、「0」は輪郭0に対応し、「1」は輪郭1に対応します。

#     
refCnts = myutils.sort_contours(refCnts)[0]
digits = {}

#           
for (i, c) in enumerate(refCnts):
    (x, y, w, h) = cv.boundingRect(c)
    roi = ref[y:y + h, x:x + w]  #          
    roi = cv.resize(roi, (57, 88))  #          
    digits[i] = roi  #                 。  ‘1'    ‘1'
これでテンプレートの処理が完了しました。
3、入力画像を前処理して、画像の数字部分を抽出する

このステップでは、クレジットカードの数字ごとに抽出し、前のステップで得られたテンプレートと一致する必要があります。まず、畳み込み核を初期化して、その後tophat操作と閉演算操作が便利です。

#       
rectKernel = cv.getStructuringElement(cv.MORPH_RECT, (9, 3))
sqKernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
次に画像を読み込み、画像のサイズを調整して、階調図に変換します。

#        ,   
card_image = cv.imread("images/credit_card_01.png")
# cv_show('a', card_image)
card_image = myutils.resize(card_image, width=300)    #       
gray = cv.cvtColor(card_image, cv.COLOR_BGR2GRAY)
# cv_show('gray', gray)

その後tophat操作を行います。tophatは写真の明るい領域を強調して、暗い部分をフィルタリングします。

tophat = cv.morphologyEx(gray, cv.MORPH_TOPHAT, rectKernel)
# cv_show('tophat', tophat)

更にソベル演算子を通してエッジを検出し、一回の閉操作、二値化を行い、もう一回の閉操作を行い、空洞を埋める。

# x   Sobel  
gradX = cv.Sobel(tophat, cv.CV_32F, 1, 0, ksize=3) 

gradX = np.absolute(gradX)  # absolute:      
min_Val, max_val = np.min(gradX), np.max(gradX)
gradX = (255 * (gradX - min_Val) / (max_val - min_Val))
gradX = gradX.astype("uint8")

#      (   ,   )       .     4    4     1  ,      
gradX = cv.morphologyEx(gradX, cv.MORPH_CLOSE, rectKernel)
# cv_show('close1', gradX)

#    
thresh = cv.threshold(gradX, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)[1]

#    ,    
thresh = cv.morphologyEx(thresh, cv.MORPH_CLOSE, sqKernel)
# cv_show('close2', thresh)

あとで輪郭を探すことができます。

threshCnts = cv.findContours(thresh.copy(), cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[0]
card_copy = card_image.copy()
cv.drawContours(card_copy, threshCnts, -1, (0, 0, 255), 2)
cv_show('Input_Contours', card_copy)

4、テンプレートマッチング
テンプレートの数字と識別される画像を全部処理してマッチングすることができます。

locs = []  #         
for i, c in enumerate(threshCnts):
    #     
    x, y, w, h = cv.boundingRect(c)

    ar = w / float(h)
    #        ,       ,             
    if 2.5 < ar < 4.0:
        if (40 < w < 55) and (10 < h < 20):
            #       
            locs.append((x, y, w, h))

#             
locs = sorted(locs, key=lambda x: x[0])
次に、各大きな輪郭を遍歴し、各大きな輪郭の中に4つの数字があり、4つの小さな輪郭に対応します。小さい輪郭をテンプレートに合わせます。

output = []  #       
for (i, (gx, gy, gw, gh)) in enumerate(locs):  #         (  4   )
    groupOutput = []

    #           (4  )
    group = gray[gy - 5:gy + gh + 5, gx - 5:gx + gw + 5]  #      
    # cv_show('group_' + str(i), group)
    #    
    group = cv.threshold(group, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)[1]  #     group
    # cv_show('group_'+str(i),group)
    #               4     
    digitCnts = cv.findContours(group.copy(), cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)[0]
    #   
    digitCnts = myutils.sort_contours(digitCnts, method="left-to-right")[0]

#                
    for c in digitCnts:  # c            
        z = 0
        #          ,resize       
        (x, y, w, h) = cv.boundingRect(c)  #     
        roi = group[y:y + h, x:x + w]  #              ,   
        roi = cv.resize(roi, (57, 88))
        # cv_show("roi_"+str(z),roi)

        #       : 0    ,1    ...
        scores = []  #      ,scores           10          

        #            
        # digits digit     0,1,...,9;digitROI          
        for (digit, digitROI) in digits.items():
            #       , res     
            res = cv.matchTemplate(roi, digitROI, cv.TM_CCOEFF)  #   roi X digitROI 0    1,2..   10 ,         
            Max_score = cv.minMaxLoc(res)[1]  #   4 ,       Maxscore
            scores.append(Max_score)  # 10    
        # print("scores:",scores)
        #         
        groupOutput.append(str(np.argmax(scores)))  #                
        z = z + 1
#    
    cv.rectangle(card_image, (gx - 5, gy - 5), (gx + gw + 5, gy + gh + 5), (0, 0, 255), 1)  #    ,   
# putText  :  ,     ,     ,  ,    ,  ,    
    cv.putText(card_image, "".join(groupOutput), (gx, gy - 15), cv.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
最後にそれをプリントアウトしたら、任務は完了します。

cv.imshow("Output_image_"+str(i), card_image)
cv.waitKey(0)

締め括りをつける
クレジットカードの識別例は画像処理の基本的な操作を使っています。CVを始めたばかりの人には友好的です。
以上はPython OpenCV数字識別ケースの詳細内容を詳しく説明しました。Python OpenCV数字識別ケースに関する資料は他の関連記事に注目してください。