2021 年度全国学部電子設計コンペティションのデジタル認識技術部門の演習および学習ノート 質問 F

第1章;序章

  • このプロジェクトはこのプロジェクトに依存していますCNN_Mobilenet_Training。完了したプロジェクトのリストで名前を探し、関連ドキュメントを読んでください。
    このプロジェクトのCSDNへの接続はこちら
    スクリーンショット 2022-01-28 00.57.34

この文書は主に、2021 年度全国学部電子設計コンテスト F の問題について説明しています。デジタル識別この技術分野、機械制御、その他の関連部品は関与しません。

この技術分野には入力と出力が必要です。入力はデジタル フォント、フィールド境界線、ガイドラインを含む画像であり、出力は認識された数値を配列で表示することです。

下の写真は大会組織委員会が提供するデジタルフォントですが、難易度を下げるために数字は8個だけ用意されています。

スクリーンショット 2022-01-28 01.05.07

第2章 制作アイデア

第1節 環境と映像の効果とアイデア

競技フィールドの背景色は白色で、環境は直射日光のない自然光照明と上部の多灯照明環境であり、公式競技中に特別な照明条件は必要ないが、前方に補助照明を設置することができるカメラの環境への影響や画質への影響を排除します。

実際の会場の撮影効果は次の図に示されています。

65

車載カメラはRaspberry Pi専用の300万画素カメラですが、センサーやレンズの品質上、不完全さ(レンズの焦点距離による)や色かぶり、ぼやけ等がある場合がございます。具体的な図を次の図に示します。

スクリーンショット 2022-01-28 01.25.25

そこで、次のようなアイデアと方法を思いつきました。

  • データセットを収集するときは、Raspberry Pi を使用して収集用のカメラに切り替えます。
  • 元データを二値化により白黒画像に変換します。
  • トレーニング中にバイナリ化されたデータセットを使用します。
  • 認識中に、キャプチャした写真を二値化します。
  • 画像のサイズを変更します。
  • 設定された境界値によって画像をセグメント化します。
  • 設定された要件を満たさない写真を削除します。
  • 残りの写真を特定します。

セクション 2 データセットの作成とトレーニング

まずはバイナリイメージというものを理解しましょう

  • カラー画像には RGB の 3 つのチャネルがあり、各チャネルには 0 ~ 255、合計 2^24 ビット空間があります。

  • グレースケール画像には 0 ~ 255 のチャネルがあるため、256 個のグレースケールがあります。

  • バイナリ画像には白と黒の 2 色しかありません

スクリーンショット 2022-01-28 02.00.26

これらの操作は OpenCV の関数を使用して行うことができますが、この部分は私が担当していないため、オリジナルのコードはありませんが、インターネットから動作するコードをいくつか見つけたので、以下に記載します。

import cv2 as cv
import numpy as np

#全局阈值
def threshold_demo(image):
    gray = cv.cvtColor(image, cv.COLOR_RGB2GRAY)  #把输入图像灰度化
    #直接阈值化是对输入的单通道矩阵逐像素进行阈值分割。
    ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_TRIANGLE)
    print("threshold value %s"%ret)
    cv.namedWindow("binary0", cv.WINDOW_NORMAL)
    cv.imshow("binary0", binary)

#局部阈值
def local_threshold(image):
    gray = cv.cvtColor(image, cv.COLOR_RGB2GRAY)  #把输入图像灰度化
    #自适应阈值化能够根据图像不同区域亮度分布,改变阈值
    binary =  cv.adaptiveThreshold(gray, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY, 25, 10)
    cv.namedWindow("binary1", cv.WINDOW_NORMAL)
    cv.imshow("binary1", binary)

#用户自己计算阈值
def custom_threshold(image):
    gray = cv.cvtColor(image, cv.COLOR_RGB2GRAY)  #把输入图像灰度化
    h, w =gray.shape[:2]
    m = np.reshape(gray, [1,w*h])
    mean = m.sum()/(w*h)
    print("mean:",mean)
    ret, binary =  cv.threshold(gray, mean, 255, cv.THRESH_BINARY)
    cv.namedWindow("binary2", cv.WINDOW_NORMAL)
    cv.imshow("binary2", binary)

src = cv.imread('E:/imageload/kobe.jpg')
cv.namedWindow('input_image', cv.WINDOW_NORMAL) #设置为WINDOW_NORMAL可以任意缩放
cv.imshow('input_image', src)
threshold_demo(src)
local_threshold(src)
custom_threshold(src)
cv.waitKey(0)
cv.destroyAllWindows()

対応するコードの実行効果は次のとおりです。

画像

トレーニング結果のヒートマップと折れ線は以下のとおりです。

IMG_0110

IMG_0111

これは、最初のトレーニング、5,000 枚、320 回のトレーニングの結果です。時間が遅すぎ、トレーニング数が少なすぎるため、一部の数値の認識率はまだ良好ではありません。

セクション 3 コンピュータ環境の構築

この部分は と整合性があるのでCNN_Mobilenet_Trainingコピーしました もちろんCUDA部分は直接飛ばしても大丈夫です。

まず、Python 3.8 と conda 環境をコンピューターにインストールする必要があります。これは anaconda を通じてインストールできます。

新しい conda 環境を作成します。yourEnvName新しい conda 環境の名前を表します。

conda create --name yourEnvName python=3.8

作成した conda 環境をアクティブ化します

windows ==> activate yourEnvName
linux/mac ==> source activate yourEnvName

よく使用される conda コマンド

conda info --envs:输出中带有【*】号的的就是当前所处的环境
conda list: 看这个环境下安装的包和版本
conda install numpy scikit-learn: 安装numpy sklearn包
conda env remove -n yourEnv: 删除你的环境
conda env list: 查看所有的环境

conda 環境のインストール方法については、個別にインストールすることも、anaconda を通じて一緒にインストールすることもできます。

Windows での開発環境はかなり特殊で、Python をインストールするには Python 公式サイトから Python インストールパッケージをダウンロードする必要があります。具体的なインストールバージョンは 3.8 ですが、実際のテストバージョンは 3.7 と 3.9 でも大きな問題はないようですが、それでもバージョン 3.8 をインストールすることをお勧めします。

Mac ユーザーと Ubuntu ユーザーは通常の方法でインストールできますが、不明な点がある場合は、Baidu または Google を使用してください。

TensorFlow が CPU バージョンと GPU バージョンのどちらでインストールされるかについては、バージョン 2.0 以降は 1 つのインストール パッケージに統合されるようです。プログラムはまず、コンピューターに正しいグラフィック カードと、対応する CUDA および CUDNN が搭載されているかどうかを確認します。動作する場合は GPU がトレーニングに使用され、動作しない場合は CPU がトレーニングに使用されます。

トレーニングの速度を確保するには、CPU が AVX-2 命令セットまたは AVX 命令セットをサポートしている必要があります。AVX 命令セットがない場合は、別のコンピュータで実行するか、AVX 命令セット プロセッサが含まれていない TensorFlow インストール パッケージを TensorFlow 公式 Web サイトからダウンロードしてインストールすることができます。私の記憶が正しければ、今回使用した TensorFlow2.3.1 にはこの特別なインストールパッケージは存在しなかったような気がします。TensorFlow の別のバージョンに変更したい場合はどうすればよいでしょうか? いいえ、このプログラムは TensorFlow バージョン 2.3.1 をサポートしています。これは、いくつかの特別な API を使用する必要があるためです。また、上位または下位のバージョンにはこの API がないため、プログラムは実行できません。したがって、これが唯一の方法です。

当時、TensorFlow を正常に実行できるようにこの環境を構成するのにほぼ 1 週間かかり、GPU 上で TensorFlow を実行できるようにするのにさらに 1 週​​間かかりました。インストールプロセスは面倒でした。それは誰かにベッドに押し倒されたような不快さだった。

スクリーンショット 2022-01-27 01.24.21

CUDAとCUDNNのインストール方法については、BaiduやCSDNを利用するか、NVIDIAやTensorFlowの公式ドキュメントを読んでください。各コンポーネントのバージョンが対応している必要があり、たとえすべてドキュメントに従っていたとしても、これは非常に非常に不快です。操作を実行できるようになります。グラフィック カード ドライバーに関係があります。最後に操作を実行したのは、ドライバーのバージョンが高すぎたためです。いいえ、ドライバーのバージョンが下げられました。エラーレポートを見るたびに、私の気分は次の図で説明できます。

src=http---b-ssl.duitang.com-uploads-item-201902-23-20190223000726_hBezt.jpeg&refer=http---b-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt= jpeg

遠く離れたところでは、要するに、インストール プロセス、ユーザー エクスペリエンス、ウォレットのいずれであっても、NVIDIA との関係が不幸になるでしょう。仕事に戻る。

使用する必要のあるライブラリファイルのリストが に作成されているのでrequirement.txt、コンピュータを変更した場合のインストールに便利です。

ここで強調しておく必要があるのは、このファイルの TensorFlow バージョンと、Raspberry Pi 上の Panda および Numpy バ​​ージョンとの間に競合があることです。インストールできない場合は、TensorFlow を最初に手動でインストールする必要があります。現時点では、他の 2 つは、これら 2 つのライブラリを手動でアップグレードして戻すと、Keras が動作しません。テストではエラーはありませんでした。

ライブラリファイルをインストールする場合、作成したconda環境では以下のコマンドでインストールできます。

pip install -r requirement.txt 

スクリーンショット 2022-01-26 22.53.32

インストールが完了すると、上記のプロンプトが表示されます。今度は使ってください

pip list

ライブラリファイルが正常にインストールされているか確認してください。

ファイルが失われた場合は、次のコードでファイルrequirement.txtを再構築できます。requirement.txt

absl-py==0.11.0
aliyun-iot-linkkit==1.2.3
astunparse==1.6.3
cachetools==4.1.1
certifi==2021.10.8
charset-normalizer==2.0.4
crcmod==1.7
cycler==0.10.0
docopt==0.6.2
gast==0.3.3
google-auth==1.23.0
google-auth-oauthlib==0.4.2
google-pasta==0.2.0
grpcio==1.34.0
h2==2.6.2
h5py==2.10.0
hpack==3.0.0
hyper==0.7.0
hyperframe==3.2.0
idna==3.2
imutils==0.5.4
keras==2.7.0
Keras-Preprocessing==1.1.2
kiwisolver==1.3.2
labelImg==1.8.6
lxml==4.6.4
Markdown==3.3.3
matplotlib==3.4.3
numpy==1.18.5
oauthlib==3.1.0
opencv-python==4.5.3.56
opt-einsum==3.3.0
paho-mqtt==1.5.1
pandas==1.3.2
Pillow==8.3.2
protobuf==3.14.0
pyasn1==0.4.8
pyasn1-modules==0.2.8
pyparsing==2.4.7
PyQt5==5.15.4
PyQt5-Qt5==5.15.2
PyQt5-sip==12.9.0
python-dateutil==2.8.2
pytz==2021.1
requests==2.26.0
requests-oauthlib==1.3.0
rsa==4.6
schedule==1.1.0
scipy==1.7.1
seaborn==0.11.2
six==1.16.0
stomp.py==7.0.0
tensorboard==2.4.0
tensorboard-plugin-wit==1.7.0
tensorflow==2.3.1
tensorflow-estimator==2.3.0
termcolor==1.1.0
tqdm==4.62.2
urllib3==1.26.6
Werkzeug==2.0.1
wrapt==1.12.1

第4節 プログラム解説

パート 1 カメラから写真を取得する

def take_pic(path,cam_v):
    cap = cv2.VideoCapture(cam_v)
    ret, frame = cap.read()
    cv2.imwrite(path, frame)
  • @path : 撮影後の画像の保存アドレス。
  • @cam_v : 呼び出すカメラの数を選択します。通常は「0」に設定されます。

パート 2 しきい値に応じて写真をトリミングする

ic.images_cut_img_input(PATH,BIN_LOW,BIN_HIGH)
PATH = './images/IMG_9281.JPG'  #要处理的图片路径
BIN_LOW = 27					#设定二值化
BIN_HIGH = 255

パラメータは以下の関数に渡されます

def images_cut_img_input(IMG_PATH,BIN_LOW,BIN_HIGH):
    img = cv.imread(IMG_PATH)
    img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)  # 函数的返回值为转换之后的图像
    # 通常为25,255
    ret, th1 = cv.threshold(img_gray, BIN_LOW, BIN_HIGH, cv.THRESH_BINARY)
    # 二值化处理之后的图片只有 0 和 255  0为黑 255 为白
    img_list = cut_image_by_projection(th1)
    count = 1
    file_operate.custom_rmdir('./predict/data/')
    #time.sleep(10)
    for i in img_list:
        # 这里可以对切割到的图片进行操作,显示出来或者保存下来
        cv.imwrite('./predict/data/'+str(count) + '.jpg', i)
        count += 1

それを通じてthreshold画像をバイナリ画像に変換します。その中のパラメータはimg_gray以下に対応しますcv.cvtColor(img, cv.COLOR_BGR2GRAY)

cvtColorこの機能は画像の色空間の変換を実現するもので、関数形式は次のとおりです。

void cvCvtColor( const CvArr* src, CvArr* dst, int code );
#每一部分参数的解释如下
src:源图像(输入的 8-bit , 16-bit 或 32-bit 单倍精度浮点数影像)

dst:目标图像(输入的 8-bit , 16-bit 或 32-bit 单倍精度浮点数影像)

code:

RGB <--> BGR:CV_BGR2BGRA、CV_RGB2BGRA、CV_BGRA2RGBA、CV_BGR2BGRA、CV_BGRA2BGR

RGB <--> 5X5:CV_BGR5652RGBA、CV_BGR2RGB555、(以此类推,不一一列举)

RGB <---> Gray:CV_RGB2GRAY、CV_GRAY2RGB、CV_RGBA2GRAY、CV_GRAY2RGBA

RGB <--> CIE XYZ:CV_BGR2XYZ、CV_RGB2XYZ、CV_XYZ2BGR、CV_XYZ2RGB

RGB <--> YCrCb(YUV) JPEG:CV_RGB2YCrCb、CV_RGB2YCrCb、CV_YCrCb2BGR、CV_YCrCb2RGB、CV_RGB2YUV(将YCrCb用YUV替代都可以)

RGB <--> HSV:CV_BGR2HSV、CV_RGB2HSV、CV_HSV2BGR、CV_HSV2RGB

RGB <--> HLS:CV_BGR2HLS、CV_RGB2HLS、CV_HLS2BGR、CV_HLS2RGB

RGB <--> CIE L*a*b*:CV_BGR2Lab、CV_RGB2Lab、CV_Lab2BGR、CV_Lab2RGB

RGB <--> CIE L*u*v:CV_BGR2Luv、CV_RGB2Luv、CV_Luv2BGR、CV_Luv2RGB

RGB <--> Bayer:CV_BayerBG2BGR、CV_BayerGB2BGR、CV_BayerRG2BGR、CV_BayerGR2BGR、CV_BayerBG2RGB、CV_BayerGB2RGB、 CV_BayerRG2RGB、CV_BayerGR2RGB(在CCD和CMOS上常用的Bayer模式)

YUV420 <--> RGB:CV_YUV420sp2BGR、CV_YUV420sp2RGB、CV_YUV420i2BGR、CV_YUV420i2RGB

ここでは 2 つのパラメーター、つまり src と code のみを使用しました。

OpenCV のデフォルトの画像チャネル順序は次のとおりであることにも注意してください。BGRいいえRGB!!!

次のステップは、画像を投影して切り取ることです

img_list = cut_image_by_projection(th1)

この機能には 3 つのカットモードがありますが、書体をより正確に切り出すために、水平と垂直の 2 つのカットを選択しました。

def cut_image_by_projection(img, cvalue=255, patern=2):
    """
    传入二值化处理之后的图片 通过投影切割获取每个单独的数字
    处理方法默认为先水平切割再竖直切割
    :param cvalue: 根据切个数值,默认为255(根据白色切割),可选择0(根据黑色切割)
    :param img:传入的二值化图片
    :param patern: 2 为水平竖直两次切割,0 为水平切割, 1 为竖直切割
    :return: 返回切割完成后的图片数组
    """
    if patern == 2:
        return cut_vertical(cut_level(img, cvalue=cvalue), cvalue=cvalue)
    elif patern == 1:
        return cut_vertical(img, cvalue=cvalue)
    else:
        return cut_level(img, cvalue=cvalue)

ここでは、白に従ってカットします。つまり、黒い境界線または黒い境界線を維持します。もちろん、それらのフィールド境界や引き出し線も含まれます。

スクリーンショット 2022-01-28 04.01.21

この模式図(二値化処理はせず、画像編集ソフトでグレースケール処理をしただけです)では、赤色のカラーブロックの部分が切り出し部分を表しており、切り出された細かい部分のうち、黒色を含む部分は保留されています。 、直接削除します。したがって、この模式図の残りの部分は、中央の「6」と「5」のデジタル フォント、フィールドのサイドライン、およびガイドラインになります。

カットした写真はpredict/dataディレクトリの下に配置されます。写真には上から下、左から右に番号が付けられています。

パート 3 フィルター写真

上映される写真の価値は、元の写真のサイズとカメラの設置位置に関係しますが、ここでの写真は 640 x 480 のサイズの 300 万画素カメラで撮影されているため、画面サイズをデジタル フォントを 70 x 80 に拡大し、スクリーンアウトします。結果と精度は悪くありません。すべての場合 (曲がっている、不完全、ぼやけている) でデジタル タイプの領域を正確に切り出すことができます。

import os
from PIL import Image
import glob
dir1 = './predict/data'  # 图片文件存放地址

def images_select(MAX_WIDTH,MAX_HEIGHT):
    paths = glob.glob(os.path.join(dir1, '*.jpg'))
    # 输出所有文件和文件夹
    for file in paths:
        fp = open(file, 'rb')
        img = Image.open(fp)
        fp.close()
        width = img.size[0]
        height = img.size[1]
        if (width <= MAX_WIDTH) or (height <= MAX_HEIGHT):
            os.remove(file)

パスの下にあるすべての写真を走査して、設定されたサイズに適合しているかどうかを判断し、適合していない写真を削除します。

パート 4 写真を識別する

これは最も重要なステップです

最初main.pyにモデルを事前にプリロードすると、モデルのロード時間を 5 ~ 6 秒短縮できます。

model = tf.keras.models.load_model("models/number_rcog_mobilenet.h5")

モデルは、パラメータを継続的に渡すことによって関数に渡されますdef start_recognize(model)

class_names = ['1','2','3','4','5','6','7','8']  # 这个数组在模型训练的开始会输出

このクラス名の具体的なソースと使用法については、CNN_Mobilenet_Trainingプロジェクトのドキュメントを参照してください。

macOS システムを使用しているため、システムは.DS_Storeという名前のファイルを自動的に生成するため、まずこのファイルがターゲット パスに存在するかどうかを確認し、存在する場合は削除する必要があります。

predict_dir = './predict/'
test = os.listdir(predict_dir)
if '.DS_Store' in test:
    test.remove('.DS_Store')

削除後、各画像のパスをimagesこの配列に保存します

	#新建一个列表保存预测图片的地址
    images = []
    #获取每张图片的地址,并保存在列表images中
    for testpath in test:  #循环获取测试路径底下需要测试的图片
        for fn in os.listdir(os.path.join(predict_dir, testpath)):
            if fn.endswith('JPG'):
                fd = os.path.join(predict_dir, testpath, fn)
                #print(fd)
                images.append(fd)
            elif fn.endswith('jpg'):
                fd = os.path.join(predict_dir, testpath, fn)
                #print(fd)
                images.append(fd)
            elif fn.endswith('png'):
                fd = os.path.join(predict_dir, testpath, fn)
                #print(fd)
                images.append(fd)

一般的に、この順序の問題は考慮する必要がありません。したがって、カットするときにルールに従って分類されています。車が所定の方法で停止または歩行しない限り、選択の余地はありません。 3泊4日の道のりで、たくさんのことを考える時間になりました。

次に、認識プログラムブロックに入ります。

result_list = []
for img in images:
    imgpath = img
    img_init = cv2.imread(img)
    img_init = cv2.resize(img_init, (224, 224))  # 将图片大小调整到224*224用于模型推理
    cv2.imwrite(img, img_init)
    img = Image.open(img)  # 读取图片
    img = np.asarray(img)  # 将图片转化为numpy的数组
    outputs = model.predict(img.reshape(1, 224, 224, 3))  # 将图片输入模型得到结果
    result_index = int(np.argmax(outputs))
    result = class_names[result_index]
    #result.setText(result)
    #return img,result #返回图片地址和识别出的数字
    imgf = imgpath.split('/')[3]
    imgb = imgf.split('.')[0]
    #print(result)
    result_list.append([imgb,result])

result_list = sorted(result_list, key=(lambda x:x[0]))

ここでプログラムについて説明する必要があります。まず、なぜ画像のサイズを変更する必要があるのか​​というと、モデルをトレーニングしたときに 224x224 を選択したからです。認識のためにモデルを呼び出すときに画像の解像度が 224x224 でない場合、プログラムはエラーを報告します。他のサイズの画像を認識に使用したい場合は、トレーニング中に設定する必要があります。

最終認識後に返される形式は次のとおりです。[[シリアル番号、認識結果]、[シリアル番号、認識結果]、[シリアル番号、認識結果]、[シリアル番号、認識結果]]

具体的な効果は下図のようになりますが、結果は「5523」となり、全く問題ありません。

IMG_0109

第3章 概要

アプリケーション シナリオの分析の間違いにより、このプログラムは Raspberry Pi 上で非常に遅く実行されます。関連するソリューションを長い間読んできました。つまり、プログラムをトレーニングするときに量子化に INT8 モデルを使用する必要があるということです。つまり、現在使用されている float 32 (float 32 か float 64 だったか忘れましたが、とにかくかなり大きいです) の代わりに 8 Bit を使用するため、トレーニング プログラムに大きな float 変更が必要になります。プログラムなのでそれを行う方法はありません。次に、トレーニングに使用するデータセットの画像サイズも適切に縮小する必要があり、解像度が 640x480 では高すぎるため、64x48 に縮小する必要があります。Raspberry Pi などの低パフォーマンスのマシンでも実行できるように、認識に使用される写真の解像度も 64x48 以下である必要があります。

つまり、このドキュメントには TensorFlow のアプリケーション、独自のトレーニング データ セットのアプリケーション、および OpenCV のアプリケーションの一部が含まれています。

いくつかのコードやアイデアが役立つことを願っています。

私のレベルに限りますが、ドキュメントやコード内に不適切な表現がありましたら、遠慮なくご指摘ください。

第 4 章全プログラム

プログラムの完全なディレクトリは次のとおりです

スクリーンショット 2022-01-28 04.05.31

モデル内のモデルはCNN_Mobilenet_Trainingプロジェクトに従ってトレーニングする必要があります。

画像内の写真の解像度は 640x480 で、一貫した名前を付ける必要があります。

これにより、プログラムが失われた場合でもプロジェクトを再構築できます。

セクション 1 main.py

import take_and_recog
import tensorflow as tf

def TwoDConverToOneD(List):
    new_list = []
    for line in List:
        new_list.append(line[1])
    return new_list

if __name__ == '__main__':
    model = tf.keras.models.load_model("models/number_rcog_mobilenet.h5")
    print(TwoDConverToOneD(take_and_recog.photo_take_and_recog(model)))

セクション 2 カメラ.py

import cv2

def take_pic(path,cam_v):
    cap = cv2.VideoCapture(cam_v)
    ret, frame = cap.read()
    cv2.imwrite(path, frame)

セクション 3 take_and_recog.py

import images_cut as ic
import images_select
import recognize_number
import camera

def photo_take_and_recog(model):
    # 要裁剪的照片的保存地址和阈值设置参数
    PATH = './images/IMG_9281.JPG'
    BIN_LOW = 27
    BIN_HIGH = 255

    # 筛选照片的长宽参数
    MAX_WIDTH = 70      #70,80
    MAX_HEIGHT = 80

    #camera.take_pic(PATH,0) #拍照保存路径和相机位
    #按照阈值设置裁剪照片
    ic.images_cut_img_input(PATH,BIN_LOW,BIN_HIGH)
    #筛选出数字牌的区域
    images_select.images_select(MAX_WIDTH,MAX_HEIGHT)

    return recognize_number.start_recognize(model)

セクション 4 image_cut.py

import cv2 as cv
import numpy as np
import file_operate
import time

def count_number(num_list, num):
    """
    统计一维数组中某个数字的个数
    :param num_list:
    :param num:
    :return: num的数量
    """
    t = 0
    for i in num_list:
        if i == num:
            t += 1
    return t


def cut_level(img, cvalue=255):
    """
    投影法水平切割一张图片 主要处理多行文本
    :param cvalue:  切割线的颜色
    :param img: 传入为一张图片
    :return: 水平切割之后的图片数组
    """
    r_list = []
    end = 0
    for i in range(len(img)):
        if count_number(img[i], cvalue) >= img.shape[1]:
            star = end
            end = i
            if end - star > 1:
                # 如果相差值大于一的时候就说明跨过待切割的区域,
                # 根据 star 和end 的值就可以获取区域
                r_list.append(img[star:end, :])
    return r_list


def cut_vertical(img_list, cvalue=255):
    """
    投影法竖直切割图片的数组
    :param img_list: 传入的数据为一个由(二维)图片构成的数组,不是单纯的图片
    :param cvalue: 切割的值 同cut_level中的cvalue
    :return: 切割之后的图片的数组
    """
    # 如果传入的是一个普通的二值化的图片,则需要首先将这个二值化的图片升维为图片的数组
    if len(np.array(img_list).shape) == 2:
        img_list = img_list[None]
    r_list = []
    for img_i in img_list:
        end = 0
        for i in range(len(img_i.T)):
            if count_number(img_i.T[i], cvalue) >= img_i.shape[0]:
                star = end
                end = i
                if end - star > 1:
                    r_list.append(img_i[:, star:end])
    return r_list


def cut_image_by_projection(img, cvalue=255, patern=2):
    """
    传入二值化处理之后的图片 通过投影切割获取每个单独的数字
    处理方法默认为先水平切割再竖直切割
    :param cvalue: 根据切个数值,默认为255(根据白色切割),可选择0(根据黑色切割)
    :param img:传入的二值化图片
    :param patern: 2 为水平竖直两次切割,0 为水平切割, 1 为竖直切割
    :return: 返回切割完成后的图片数组
    """
    if patern == 2:
        return cut_vertical(cut_level(img, cvalue=cvalue), cvalue=cvalue)
    elif patern == 1:
        return cut_vertical(img, cvalue=cvalue)
    else:
        return cut_level(img, cvalue=cvalue)


def images_cut_img_input(IMG_PATH,BIN_LOW,BIN_HIGH):
    img = cv.imread(IMG_PATH)
    img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)  # 函数的返回值为转换之后的图像
    # 通常为25,255
    ret, th1 = cv.threshold(img_gray, BIN_LOW, BIN_HIGH, cv.THRESH_BINARY)
    # 二值化处理之后的图片只有 0 和 255  0为黑 255 为白
    img_list = cut_image_by_projection(th1)
    count = 1
    file_operate.custom_rmdir('./predict/data/')
    #time.sleep(10)
    for i in img_list:
        # 这里可以对切割到的图片进行操作,显示出来或者保存下来
        cv.imwrite('./predict/data/'+str(count) + '.jpg', i)
        count += 1

セクション 5 file_operate.py

import os
import shutil

def custom_rmdir(rootdir):
    filelist=os.listdir(rootdir)
    for f in filelist:
      filepath = os.path.join( rootdir, f )
      if os.path.isfile(filepath):
        os.remove(filepath)
        #print(filepath+" removed!")
      elif os.path.isdir(filepath):
        shutil.rmtree(filepath,True)
        #print("dir "+filepath+" removed!")

セクション 6 image_select.py

import os
from PIL import Image
import glob
dir1 = './predict/data'  # 图片文件存放地址

def images_select(MAX_WIDTH,MAX_HEIGHT):
    paths = glob.glob(os.path.join(dir1, '*.jpg'))
    # 输出所有文件和文件夹
    for file in paths:
        fp = open(file, 'rb')
        img = Image.open(fp)
        fp.close()
        width = img.size[0]
        height = img.size[1]
        if (width <= MAX_WIDTH) or (height <= MAX_HEIGHT):
            os.remove(file)

セクション 7 認識番号.py

import os
import numpy as np
import cv2
from PIL import Image


def start_recognize(model):
    model.summary() #输出模型各层的参数状况
    class_names = ['1','2','3','4','5','6','7','8']  # 这个数组在模型训练的开始会输出
    #要预测的图片保存在这里
    predict_dir = './predict/'
    test = os.listdir(predict_dir)
    if '.DS_Store' in test:
        test.remove('.DS_Store')
    #新建一个列表保存预测图片的地址
    images = []
    #获取每张图片的地址,并保存在列表images中
    for testpath in test:  #循环获取测试路径底下需要测试的图片
        for fn in os.listdir(os.path.join(predict_dir, testpath)):
            if fn.endswith('JPG'):
                fd = os.path.join(predict_dir, testpath, fn)
                #print(fd)
                images.append(fd)
            elif fn.endswith('jpg'):
                fd = os.path.join(predict_dir, testpath, fn)
                #print(fd)
                images.append(fd)
            elif fn.endswith('png'):
                fd = os.path.join(predict_dir, testpath, fn)
                #print(fd)
                images.append(fd)
    result_list = []
    for img in images:
        imgpath = img
        #print(img)
        img_init = cv2.imread(img)
        img_init = cv2.resize(img_init, (224, 224))  # 将图片大小调整到224*224用于模型推理
        cv2.imwrite(img, img_init)
        img = Image.open(img)  # 读取图片
        img = np.asarray(img)  # 将图片转化为numpy的数组
        outputs = model.predict(img.reshape(1, 224, 224, 3))  # 将图片输入模型得到结果
        result_index = int(np.argmax(outputs))
        result = class_names[result_index]
        #result.setText(result)
        #return img,result #返回图片地址和识别出的数字

        imgf = imgpath.split('/')[3]
        imgb = imgf.split('.')[0]
        #print(result)
        result_list.append([imgb,result])

    result_list = sorted(result_list, key=(lambda x:x[0]))

    return result_list #返回二维列表,第一项是照片顺序大小,第二项是识别出后的数字

おすすめ

転載: blog.csdn.net/qq_17790209/article/details/122726160