記事ディレクトリ
第1章;序章
- このプロジェクトはこのプロジェクトに依存しています
CNN_Mobilenet_Training
。完了したプロジェクトのリストで名前を探し、関連ドキュメントを読んでください。
このプロジェクトのCSDNへの接続はこちら
この文書は主に、2021 年度全国学部電子設計コンテスト F の問題について説明しています。デジタル識別この技術分野、機械制御、その他の関連部品は関与しません。
この技術分野には入力と出力が必要です。入力はデジタル フォント、フィールド境界線、ガイドラインを含む画像であり、出力は認識された数値を配列で表示することです。
下の写真は大会組織委員会が提供するデジタルフォントですが、難易度を下げるために数字は8個だけ用意されています。
第2章 制作アイデア
第1節 環境と映像の効果とアイデア
競技フィールドの背景色は白色で、環境は直射日光のない自然光照明と上部の多灯照明環境であり、公式競技中に特別な照明条件は必要ないが、前方に補助照明を設置することができるカメラの環境への影響や画質への影響を排除します。
実際の会場の撮影効果は次の図に示されています。
車載カメラはRaspberry Pi専用の300万画素カメラですが、センサーやレンズの品質上、不完全さ(レンズの焦点距離による)や色かぶり、ぼやけ等がある場合がございます。具体的な図を次の図に示します。
そこで、次のようなアイデアと方法を思いつきました。
- データセットを収集するときは、Raspberry Pi を使用して収集用のカメラに切り替えます。
- 元データを二値化により白黒画像に変換します。
- トレーニング中にバイナリ化されたデータセットを使用します。
- 認識中に、キャプチャした写真を二値化します。
- 画像のサイズを変更します。
- 設定された境界値によって画像をセグメント化します。
- 設定された要件を満たさない写真を削除します。
- 残りの写真を特定します。
セクション 2 データセットの作成とトレーニング
まずはバイナリイメージというものを理解しましょう
-
カラー画像には RGB の 3 つのチャネルがあり、各チャネルには 0 ~ 255、合計 2^24 ビット空間があります。
-
グレースケール画像には 0 ~ 255 のチャネルがあるため、256 個のグレースケールがあります。
-
バイナリ画像には白と黒の 2 色しかありません;
これらの操作は 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()
対応するコードの実行効果は次のとおりです。
トレーニング結果のヒートマップと折れ線は以下のとおりです。
これは、最初のトレーニング、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 週間かかりました。インストールプロセスは面倒でした。それは誰かにベッドに押し倒されたような不快さだった。
CUDAとCUDNNのインストール方法については、BaiduやCSDNを利用するか、NVIDIAやTensorFlowの公式ドキュメントを読んでください。各コンポーネントのバージョンが対応している必要があり、たとえすべてドキュメントに従っていたとしても、これは非常に非常に不快です。操作を実行できるようになります。グラフィック カード ドライバーに関係があります。最後に操作を実行したのは、ドライバーのバージョンが高すぎたためです。いいえ、ドライバーのバージョンが下げられました。エラーレポートを見るたびに、私の気分は次の図で説明できます。
遠く離れたところでは、要するに、インストール プロセス、ユーザー エクスペリエンス、ウォレットのいずれであっても、NVIDIA との関係が不幸になるでしょう。仕事に戻る。
使用する必要のあるライブラリファイルのリストが に作成されているのでrequirement.txt
、コンピュータを変更した場合のインストールに便利です。
ここで強調しておく必要があるのは、このファイルの TensorFlow バージョンと、Raspberry Pi 上の Panda および Numpy バージョンとの間に競合があることです。インストールできない場合は、TensorFlow を最初に手動でインストールする必要があります。現時点では、他の 2 つは、これら 2 つのライブラリを手動でアップグレードして戻すと、Keras が動作しません。テストではエラーはありませんでした。
ライブラリファイルをインストールする場合、作成したconda環境では以下のコマンドでインストールできます。
pip install -r requirement.txt
インストールが完了すると、上記のプロンプトが表示されます。今度は使ってください
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)
ここでは、白に従ってカットします。つまり、黒い境界線または黒い境界線を維持します。もちろん、それらのフィールド境界や引き出し線も含まれます。
この模式図(二値化処理はせず、画像編集ソフトでグレースケール処理をしただけです)では、赤色のカラーブロックの部分が切り出し部分を表しており、切り出された細かい部分のうち、黒色を含む部分は保留されています。 、直接削除します。したがって、この模式図の残りの部分は、中央の「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」となり、全く問題ありません。
第3章 概要
アプリケーション シナリオの分析の間違いにより、このプログラムは Raspberry Pi 上で非常に遅く実行されます。関連するソリューションを長い間読んできました。つまり、プログラムをトレーニングするときに量子化に INT8 モデルを使用する必要があるということです。つまり、現在使用されている float 32 (float 32 か float 64 だったか忘れましたが、とにかくかなり大きいです) の代わりに 8 Bit を使用するため、トレーニング プログラムに大きな float 変更が必要になります。プログラムなのでそれを行う方法はありません。次に、トレーニングに使用するデータセットの画像サイズも適切に縮小する必要があり、解像度が 640x480 では高すぎるため、64x48 に縮小する必要があります。Raspberry Pi などの低パフォーマンスのマシンでも実行できるように、認識に使用される写真の解像度も 64x48 以下である必要があります。
つまり、このドキュメントには TensorFlow のアプリケーション、独自のトレーニング データ セットのアプリケーション、および OpenCV のアプリケーションの一部が含まれています。
いくつかのコードやアイデアが役立つことを願っています。
私のレベルに限りますが、ドキュメントやコード内に不適切な表現がありましたら、遠慮なくご指摘ください。
第 4 章全プログラム
プログラムの完全なディレクトリは次のとおりです
モデル内のモデルは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 #返回二维列表,第一项是照片顺序大小,第二项是识别出后的数字