介绍
从 2020 年左右开始,我们的生活因新冠病毒的感染而彻底改变。其中,如何佩戴口罩的问题每天都在新闻中出现。因此,这一次,我尝试开发一个系统,使用 OpenCV 和深度学习来实时确定是否贴有口罩。
开发环境
- macOS Monterey 版本 12.5.1
- Python 3.9
- Atom 版本 1.60.0
程序
- 收集不戴口罩的人和戴口罩的人的图像
- 提取面部零件
- 构建模型,评估预测准确性
- 在窗口中显示来自摄像头的输入图像
- 显示判断窗口中是否有遮罩的结果
1.采集不戴口罩的人和戴口罩的人的图片
建立模型时要做的第一件事是准备数据。这项任务花费的时间最多。特别是,在提取戴口罩的人的图像时,我在选择搜索词时经历了很多试验和错误。 “面具”这个词并不顺利,出现了只有产品的图像,出现了职业摔跤手戴的面具。我想不惜一切代价收集“戴口罩的人”的图像,并从东京的感染人数经常出现在新闻中的事实中得到暗示,戴口罩和戴口罩的人的图像是“东京口罩”我决定搜索。然后,我能够收集到如下图所示的非常好的图像。
另一方面,通过搜索“合影”收集了“不戴口罩的人”的图像。
当我检查它时,我很幸运地附上了一张乃木坂46的合影。作为收集图像的模块爬虫我用了
$ pip install icrawler $ pip list | grep icrawler icrawler 0.6.6这是收集图像的代码(scraping.py)。
“合影”和“东京面具”被指定为关键词。import numpy as np import pandas as pd import matplotlib.pyplot as plt from icrawler.builtin import BingImageCrawler import os import re from PIL import Image import glob # 画像を収集するメソッド # 引数は画像を保存するパスpath、検索ワードkeyword、収集する枚数num def scraping(path, keyword, num): bing_crawler=BingImageCrawler( downloader_threads=4, storage={'root_dir': path} ) #検索ワードにkeywordを入れたときに得られる画像をnum枚収集 bing_crawler.crawl( keyword=keyword, max_num=num ) print(f'{keyword}: scraping completed!') #ファイルの形式はjpegなので、ファイル名には必ず拡張子.jpgがつく gather_path='./gather/*.jpg' mask_people_path='./mask_people/*.jpg' keywords=['集合写真', 'マスク 東京'] num=300 scraping('./image/gather/', keywords[0], num) scraping('./image/mask_people/', keywords[1], num)2.面部提取
让模型学习从之前收集的图像中仅提取人脸部分的图像。
这是做什么:这里我们使用数据库是一个模块。
$ pip install dlib $ pip list | grep dlib dlib 19.24.0这是提取面部部分的代码(face_cut.py)。
#Dlibを始める(Dlibは画像から顔の検出をするためのツール) detector=dlib.get_frontal_face_detector() #顔画像を取得して保存 def get_face(fname): global fid img=cv2.imread(fname) #画像のサイズが大きい時はリサイズする if flag_resize: img=cv2.resize(img, None, fx=0.2, fy=0.2) #顔検出, 検出した部分の矩形の座標を取得している dets=detector(img, 1) for k,d in enumerate(dets): pprint.pprint(d) x1=int(d.left()) y1=int(d.top()) x2=int(d.right()) y2=int(d.bottom()) im=img[y1:y2, x1:x2] #50 x 50にリサイズ try: im=cv2.resize(im, (50,50)) # print('resized!') except: # print('Not resized!') continue #保存する out=outdir+"/"+str(fid)+".jpg" cv2.imwrite(out, im) fid+=1dlib中get_frontal_face_detector函数的返回值显示了检测到人脸时的坐标。
3. 模型构建,预测精度评估
使用从面部提取的图像训练模型。
这次张量流我建立了一个 CNN(卷积神经网络)。
关于 CNN,下面的网站很容易理解。
https://postd.cc/how-do-convolutional-neural-networks-work/由于本次要处理的图像是彩色的,所以图像的大小为垂直 x 水平 x 通道数是。
0 表示无掩码,1 表示掩码。
该模型使用 250 个训练数据和 50 个测试数据进行训练。
(由于带口罩和不带口罩的图像数量不同,无法识别,所以我们使用了相同数量的图像。)
这是构建模型并评估预测准确性的代码 (make_model.py)。import tensorflow.keras as keras from keras.models import Sequential from keras.layers import Dense, Dropout, Flatten from keras.layers import Conv2D, MaxPooling2D from keras.optimizers import RMSprop import matplotlib.pyplot as plt import cv2, glob import numpy as np import warnings warnings.simplefilter('ignore') print("modules imported!>>>>>>>>>>>>>>>>>>>>>>") #画像形式の指定 in_shape=(50, 50, 3) nb_classes=2 #CNNモデル構造の定義 #入力層:50x50x3ch #畳み込み層1: 3x3のカーネルを32個使う #畳み込み層2: 3x3のカーネルを32個使う #プーリング層1: 2x2で区切ってその中の最大値を使う model=Sequential() model.add(Conv2D(32, kernel_size=(3,3), activation='relu', input_shape=in_shape)) model.add(Conv2D(32, (3,3), activation='relu')) model.add(MaxPooling2D(pool_size=(2,2))) model.add(Dropout(0.25)) #畳み込み層3: 3x3のカーネルを64個使う #畳み込み層4: 3x3のカーネルを64個使う #プーリング層2: 2x2で区切ってその中の最大値を使う model.add(Conv2D(64, (3,3), activation='relu')) model.add(Conv2D(64, (3,3), activation='relu')) model.add(MaxPooling2D(pool_size=(2,2))) model.add(Dropout(0.25)) #全結合層: 512 #出力層: 2(マスクありorなしの2値) model.add(Flatten()) model.add(Dense(512, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(nb_classes, activation='softmax')) #モデルのコンパイル model.compile( loss='categorical_crossentropy', optimizer=RMSprop(), metrics=['accuracy'] ) print('model compiled!>>>>>>>>>>>>>>>>>>>>') #画像データをNumpy形式に変換 x=[] y=[] def read_files(target_files, y_val): files=glob.glob(target_files) for fname in files: # print(fname) #画像の読み出し img=cv2.imread(fname) #画像サイズを50 x 50にリサイズ img=cv2.resize(img, (50,50)) # print(img) x.append(img) y.append(np.array(y_val)) #ディレクトリ内の画像を集める read_files("./image/mask_off/*.jpg", [1,0]) read_files("./image/mask_on/*.jpg", [0,1]) x_train, y_train=(np.array(x), np.array(y)) #テスト用画像をNumpy形式で得る x,y=[[],[]] read_files("./image/mask_off_test/*.jpg", [1,0]) read_files("./image/mask_on_test/*.jpg", [0,1]) x_test, y_test=(np.array(x), np.array(y)) #データの学習 hist=model.fit( x_train, y_train, batch_size=100, epochs=100, validation_data=(x_test,y_test) ) #データの評価 score=model.evaluate(x_test, y_test, verbose=1) print("正解率 = ",score[1], 'loss = ',score[0]) #モデルの保存 model.save('mask_model.h5') #学習の様子を可視化 plt.plot(hist.history['accuracy']) plt.plot(hist.history['val_accuracy']) plt.title('Accuracy') plt.legend(['train','test'], loc='upper left') plt.show()模型.add定义 CNN 的架构(详细信息,例如类型、层数、单元数等)。
模型.编译定义了损失函数、最优算法和评估指标。现在可以用CNN作为模型。
藻湖节省将模型保存为文件。可以将模型加载到单独的文件中并按原样使用。当我知道这件事时,我很感动。这是可视化学习状态的结果。
Epoch 100/100 4/4 [==============================] - 3s 701ms/step - loss: 3.8839e-04 - accuracy: 1.0000 - val_loss: 0.4414 - val_accuracy: 0.9667 3/3 [==============================] - 0s 48ms/step - loss: 0.4414 - accuracy: 0.9667 正解率 = 0.9666666388511658 loss = 0.44139522314071655有时准确率突然下降,但最终准确率在 97% 左右。我觉得如果要获得更高的精度,选择图像数据比调整参数更重要。
以上内容进展顺利,但一开始我是在学习没有指定张数。
结果,由于没有口罩的图像数量非常多,导致过度学习,即使戴口罩也无法识别人脸。4.在窗口中显示来自摄像机的输入图像
现在模型已经完成,我准备了一个窗口显示 PC 的内置摄像头捕捉到的内容。
这里是 OpenCV视频捕捉功能我用了
(下面的命令是python版本的安装命令)$pip list | grep opencv-python opencv-python 4.6.0.66代码如下。令我惊讶的是,只要这样做,我就可以启动 PC 内置的摄像头,并在窗口中显示我在那里拍摄的内容。
import cv2 # VideoCapture オブジェクトを取得します # デフォルトでは0が内蔵カメラのIDだが、Snap Cameraを使用している影響でIDは1となっていた # editorのterminalではなく、単体のterminalから実行するとできる capture = cv2.VideoCapture(1) # capture関数からカメラからの読み込みができているか判定 # isOpend関数の戻り値はTrue, False # print(capture.isOpened()) while(True): #read関数の引数はretとframe #retは読み込みができているかの判定でTrue, False #frameはカメラが捉えた情報 ret, frame = capture.read() #ウィンドウに表示 cv2.imshow('Mask Checker',frame) #qをキーボードで入力するとカメラ停止 if cv2.waitKey(1) & 0xFF == ord('q'): break #メモリを解放する capture.release() #ウィンドウを閉じる cv2.destroyAllWindows()唯一让我着迷的是指定 VideoCapture 函数的参数。默认情况下,PC 的内置相机 ID 为 0,所以我认为如果我将参数设置为 0 会起作用,但它不起作用。就我而言,我将 SnapCamera 与 ZOOM 一起使用,因此它似乎被识别为外部摄像头。因此,我可以通过将参数设置为 1 来移动它。
5.在窗口中显示掩码存在判断结果
现在模型和相机都准备好了,终于到了确定是否有面具的时候了。让我们首先在这里定义规格。
- 如果您戴着口罩,请在您的脸上戴上绿框并说“OK”
- 如果您没有戴口罩,则会在您的脸部周围出现一个红框并显示“NO MASK!”
- 将判断结果图片保存到目录
这是代码(live_check.py)。
import tensorflow.keras as keras from keras.models import load_model import cv2, dlib, pprint, os import numpy as np #結果ラベル res_labels=['NO MASK!', 'OK'] save_dir="./live" #保存した学習モデルを読み込む model=load_model('mask_model.h5') #dlibを始める detector=dlib.get_frontal_face_detector() #webカメラから入力を開始 red=(0,0,255) green=(0,255,0) fid=1 capture=cv2.VideoCapture(1) while True: #カメラの画像を読み込む ok, frame=capture.read() if not ok: break #画面を縮小表示する frame=cv2.resize(frame, (800,600)) #顔検出 dets=detector(frame, 1) for k,d in enumerate(dets): pprint.pprint(d) x1=int(d.left()) y1=int(d.top()) x2=int(d.right()) y2=int(d.bottom()) #顔部分を切り取る im=frame[y1:y2, x1:x2] im=cv2.resize(im, (50,50)) im=im.reshape(-1, 50, 50, 3) #予測 res=model.predict([im])[0] v=res.argmax() print(res_labels[v]) #枠を描画, マスクない時(v=0)は赤で強調する color=green if v==1 else red border=2 if v==1 else 5 cv2.rectangle(frame, (x1,y1), (x2,y2), color, thickness=border) #テキストを描画 cv2.putText(frame, res_labels[v], (x1,y1-5), cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, thickness=1) #結果を保存 if len(dets)>0: if os.path.exists(save_dir): jpgfile=save_dir+"/"+str(fid)+".jpg" cv2.imwrite(jpgfile, frame) fid+=1 #ウィンドウに画像を出力 cv2.imshow('Mask Live Check', frame) #ESC or Enterキーでループ脱出 k=cv2.waitKey(1) if k==13 or k==27: break #カメラを開放 capture.release() #ウィンドウを破棄 cv2.destroyAllWindows()带面具
没有面具
虽然有时根据房间的亮度等环境而无法正确识别它,但我能够确认它运行良好。
概括
这样做很有趣,因为我经常在日常生活中看到它。另一方面,我能够感受到选择图像以提高准确性的难度和重要性。由于 Python 的处理速度很慢,因此在窗口中显示的外观中看到了延迟。我觉得有必要使用处理速度高的C++,以强调实时性。
[参考资料]
【源代码】
原创声明:本文系作者授权爱码网发表,未经许可,不得转载;
原文地址:https://www.likecs.com/show-308629805.html