本文很大程度的參考了知乎大大的文章:
<使用 python3+OpenCV+TensorFlow 做手勢識別>
原文鏈接:https://zhuanlan.zhihu.com/p/56699632
說明:
由於原文章是一年前的內容,有些內容已經不適用,存在比較多坑點,在此重新做下注解和補充;
該文章覺得非常適合初學小白入門,用來練手實踐,體驗人工“智障”的樂趣;
所以在此花了幾天時間特意整理了內容,希望能給看博客的你,種又白嫖了的快感~
一、環境配置
1、Anaconda3 下載
Windows 用戶,強烈建議使用 Anaconda3 的Python 虛擬環境來安裝tensorflow;
出問題或者啥不順心或突然暴躁的,完完全全可以直接干掉環境重新再來;誰還不是個寶寶呢~(`へ´*)ノ~
官網:https://www.anaconda.com/distribution/
Anaconda3 都支持3.7 了,直接安裝最新版本,下載 Python 3.7 version;
2、Anaconda3
建議安裝在D盤,D:\Anaconda3 下,下面就可以開啟 CTRL+C 、CTRL+V無腦模式;
3、環境變量配置;例如:安裝在D盤目錄下;
D:\Anaconda3\Scripts
D:\Anaconda3\Library\bin
D:\Anaconda3\Scripts
D:\Anaconda3\
4、 win+R 打開cmd,檢查安裝結果;
① 檢測anaconda環境是否安裝成功:conda --version
② 檢測目前安裝了哪些環境變量:conda info --envs
③ 查看當前有哪些可以使用的tensorflow版本:conda search --full --name tensorflow、
④ 查看tensorflow包信息及依賴關系:conda info tensorflow
如圖demo:
二、tensorflow安裝與模塊導入
1、安裝Git
官網下載安裝:https://gitforwindows.org/
默認安裝,安裝完成后配置環境變量;
2、下載模塊 (注:此處要與對應發tensorflow 版本一致,此次demo主要以tensorflow1.12.0版本演示)
git clone -b r1.12.0 https://github.com/tensorflow/models.git D:/tensorflow/models
---大概600Mb 左右,漫長的等待……
##注意,使用其他tensorflow 版本的同學,記得下載的模塊版本分支要與tensorflow版本一致;否則會出現很多奇奇怪怪的問題;
##例如:代碼v1、v2 啥1.x 和2.x 不兼容問題,concat函數啥啥啥寫法不兼容問題;
3、Python虛擬環境的搭建
進入windows命令模式,創建TensorFlow env環境;
由於該教程演示是用tensorflow1.12 做的,在此,直接把虛擬環境名稱命名為tensorflow1.12
安裝python3.6: conda create --name tensorflow1.12 python=3.6
刪除python3.6: conda remove --name tensorflow1.12 --all
激活tensorflow1.12環境: activate tensorflow1.12
退出tensorflow1.12環境: deactivate
4、安裝tensorflow與opencv包
① tensorflow包的安裝
1)激活tensflow的tfenv環境:
activate tensorflow1.12
2)安裝tensorflow1.12 :
pip install --upgrade --ignore-installed tensorflow==1.12.0
3)驗證功能正常:python 進入代碼環境
import tensorflow as tf hello = tf.constant('hello,tf') sess = tf.Session() print(sess.run(hello))
常見報錯問題總結:
A、tensorflow import 失敗報錯問題 溫馨提示:如果你的conda和tensorflow環境都是安裝成功的,但是一用測試代碼進行跑的時候就出問題了,那么注意,這個原因你由於你在安裝tensorflow的時候,是直接在cmd下,而不是在你用conda激活的一個環境,所以導致,tensorflow並沒有直接嵌入到conda環境,所以,就導致無法導入模塊的一個錯誤; 解決方法: (1)只需要在activate tensorflow1.12 (2)然后再使用 pip install --upgrade --ignore-installed tensorflow==1.12.0命令安裝就可以了 B、FutureWarning: Passing (type, 1) 報錯問題 使用TensorFlow時報錯FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy....... 報錯原因:numpy1-17-0版本過高,使用numpy-1.16-0版本即可 解決方法:重新安裝numpy-1.16-0 pip install numpy==1.16.0 C、 CPU 指令集報錯問題 使用TensorFlow模塊時,彈出錯誤Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX AVX2 有兩種解決辦法: 1.忽略這個警告,不看它! import os os.environ["TF_CPP_MIN_LOG_LEVEL"]='1' # 這是默認的顯示等級,顯示所有信息 os.environ["TF_CPP_MIN_LOG_LEVEL"]='2' # 只顯示 warning 和 Error os.environ["TF_CPP_MIN_LOG_LEVEL"]='3' # 只顯示 Error 我們用第二個就可以了。 2.徹底解決,換成支持cpu用AVX2編譯的TensorFlow版本。 首先,卸載原來版本的TensorFlow EG:pip install tensorflow-1.6.0-cp36-cp36m-win_amd64.whl 參考:https://github.com/fo40225/tensorflow-windows-wheel
5、模塊安裝
首先把需要使用的模塊安裝好
python -m pip install opencv-python python -m pip install Cython python -m pip install contextlib2 python -m pip install pillow python -m pip install lxml python -m pip install jupyter python -m pip install matplotlib
6、tensorflow object detection API 框架搭建
① protoc 下載
鏈接:https://pan.baidu.com/s/19xzP6_x5Ru5y_qra3wtakA
提取碼:nf6n
② 使用protoc生成python文件
然后win+R→cmd→
C:\>cd /d D:\tensorflow\models\research D:\tensorflow\models\research>D:/tensorflow/protoc-3.4.0-win32/bin/protoc object_detection/protos/*.proto --python_out=.
③ API測試准備
找到python的安裝路徑,我的虛擬環境路徑:D:\Anaconda3\envs\tensorflow1.12\Lib\site-packages;
在該文件夾下新建一個txt文件,打開該txt文件,輸入以下三條路徑
D:\tensorflow\models\research
D:\tensorflow\models\research\slim
D:\tensorflow\models\research\object_detection
將該文件命名為tensorflow_model.pth儲存即可。
④ API測試
回到cmd,輸入
cd /d D:\tensorflow\models\research
python object_detection/builders/model_builder_test.py
##得到ok,測試成功!此時tensorflow object detection API 框架搭建完成。
三、手勢數據的收集與標注
1、數據收集
① 提前工作
在D盤tensorflow目錄下,創建一個文件夾,命名為hand_data;在hand_data里創建一個文件夾,命名為VOC2012(注意:該名稱必須為VOC2012);在VOC2012文件夾下創建3個文件夾,分別命名為Annotations,ImageSets,JPEGImages(注意:該名稱必須為Annotations,ImageSets,JPEGImages); 在ImageSets文件夾內創建Main文件夾。
回到cmd,輸入
md d:\tensorflow\hand_data\VOC2012\Annotations md d:\tensorflow\hand_data\VOC2012\ImageSets md d:\tensorflow\hand_data\VOC2012\JPEGImages\Main
② 使用攝像頭+opencv收集數據
以下代碼設定的為每5幀保存一次,可以修改,保存地址為VOC2012文件夾下的JPEGImages文件夾內,每種手勢收集1000張圖片最佳。
import cv2 as cv import numpy as np capture = cv.VideoCapture(0) index = 1 while True: ret, frame = capture.read() if ret is True: cv.imshow("frame", frame) index += 1 if index % 5 == 0: cv.imwrite("D:/tensorflow/hand_data/VOC2012/JPEGImages/" + str(index) + ".jpg", frame) c = cv.waitKey(50) if c == 27: # 退出命令鍵盤 Esc break else: break cv.destroyAllWindows()
首先刪除不清晰的圖片,因為收集到的圖片的序號不是按照順序排的,因此需要排好序再標注。在JPEGImages文件夾內新建一個txt文件,把以下代碼copy進去,命名為.bat文件,保存好,再運行一下,圖片名就會按照順序排列好了。
@echo off set a=0 setlocal EnableDelayedExpansion dir /b .\*.jpg | find /c /v "" >> .\tmp.txt set /p c=<.\tmp.txt del /a /f /q .\tmp.txt for %%i in (*.jpg) do ( set /a a+=1 if !a! gtr %c% (goto aa) echo !a! echo %%i ren "%%i" "!a!.jpg" ) :aa pause
2、數據標注
① labelImg下載
百度雲盤鏈接:https://pan.baidu.com/s/1Ros4T5c-m401urzt_NdVNw 提取碼:xdiz
② 添加標簽
下載解壓完成后打開
本文對三個手勢進行標注,在該txt文件中添加了三個標簽
把儲存照片的JPEGImages文件夾路徑設置好:
路徑都設置好以后,再按下W即可對圖片標注。
標注好位置后,點擊save進行保存。(快捷方式為兩次空格鍵)(也可以按一次空格鍵,全部標注完以后,檢查時再按一次空格鍵方便檢查,會有綠色和白色的背景色提醒,白色為最終確認的顏色)
然后點擊Next Image 對下一張圖片進行數據標注。(快捷方式為D)
想返回上一張查看的話,可以點擊Prev Image。(快捷方式為A)
重復上述工作至全部標注完成即可。數據標注工作枯燥無味,如果一次未能全部標注完,關閉后下一次可以繼續標注。
③ xml文件的修改
之前標注好的數據會保存在.xml文件中,但是格式並不完全正確。下面紅色方框內的內容必須要改成如下形式即可。
上千個.xml文件,手動改是不可能手動改的。用下面的python代碼可以遍歷全部文件進行更改,並且在Main文件夾內保存對應的txt文件。
import cv2 as cv import os from xml.dom import minidom import xml.etree.ElementTree as ET root_dir = "D:/tensorflow/hand_data/VOC2012/JPEGImages/" def xml_modification(): ann_dir = "D:/tensorflow/hand_data/VOC2012/Annotations/" files = os.listdir(ann_dir) for xml_file in files: if os.path.isfile(os.path.join(ann_dir, xml_file)): xml_path = os.path.join(ann_dir, xml_file) tree = ET.parse(xml_path) root = tree.getroot()
# changing a field text for elem in root.iter('folder'): elem.text = 'voc2012' for elem in root.iter('name'): name = elem.text elem.text = name.replace(" ", "") tree.write(xml_path) print("processed xml : %s" % (xml_path)) def generate_classes_text(): print("start to generate classes text...") ann_dir = "D:/tensorflow/hand_data/VOC2012/Annotations/" okay_train = open("D:/tensorflow/hand_data/VOC2012/ImageSets/Main/Okay_train.txt", 'w') okay_val = open("D:/tensorflow/hand_data/VOC2012/ImageSets/Main/Okay_val.txt", 'w') good_train = open("D:/tensorflow/hand_data/VOC2012/ImageSets/Main/Good_train.txt", 'w') good_val = open("D:/tensorflow/hand_data/VOC2012/ImageSets/Main/Good_val.txt", 'w') v_train = open("D:/tensorflow/hand_data/VOC2012/ImageSets/Main/V_train.txt", 'w') v_val = open("D:/tensorflow/hand_data/VOC2012/ImageSets/Main/V_val.txt", 'w') files = os.listdir(ann_dir) for xml_file in files: if os.path.isfile(os.path.join(ann_dir, xml_file)): xml_path = os.path.join(ann_dir, xml_file) tree = ET.parse(xml_path) root = tree.getroot() for elem in root.iter('filename'): filename = elem.text for elem in root.iter('name'): name = elem.text if name == "Okay": okay_train.write(filename.replace(".jpg", " ") + str(1) + "\n") okay_val.write(filename.replace(".jpg", " ") + str(1) + "\n") good_train.write(filename.replace(".jpg", " ") + str(-1) + "\n") good_val.write(filename.replace(".jpg", " ") + str(-1) + "\n") v_train.write(filename.replace(".jpg", " ") + str(-1) + "\n") v_val.write(filename.replace(".jpg", " ") + str(-1) + "\n") if name == "V": okay_train.write(filename.replace(".jpg", " ") + str(-1) + "\n") okay_val.write(filename.replace(".jpg", " ") + str(-1) + "\n") good_train.write(filename.replace(".jpg", " ") + str(-1) + "\n") good_val.write(filename.replace(".jpg", " ") + str(-1) + "\n") v_train.write(filename.replace(".jpg", " ") + str(1) + "\n") v_val.write(filename.replace(".jpg", " ") + str(1) + "\n") if name == "Good": okay_train.write(filename.replace(".jpg", " ") + str(-1) + "\n") okay_val.write(filename.replace(".jpg", " ") + str(-1) + "\n") good_train.write(filename.replace(".jpg", " ") + str(1) + "\n") good_val.write(filename.replace(".jpg", " ") + str(1) + "\n") v_train.write(filename.replace(".jpg", " ") + str(-1) + "\n") v_val.write(filename.replace(".jpg", " ") + str(-1) + "\n") okay_train.close() okay_val.close() v_train.close() v_val.close() good_train.close() good_val.close() xml_modification() #generate_classes_text()
成功運行上面python代碼后,得到以下txt文件。
對得到的txt文件進行說明:第一列為圖片的名稱,第二列為圖片的結果,-1代表不是,1代表是。
至此,VOC2012數據集制作完成。
④ tf.record數據的生成
在tensorflow文件夾下新建一個文件夾,命名為hand_set,在hand_set文件夾下新建三個文件夾,分別命名為data,export,model。
md d:\tensorflow\hand_set\data
md d:\tensorflow\hand_set\export
md d:\tensorflow\hand_set\model
在model文件夾下新建eval和train文件夾。
md d:\tensorflow\hand_set\model\eval
md d:\tensorflow\hand_set\model\train
在hand_data文件夾里新建一個txt文件,命名為hand_label_map.pbtxt,
在D:\tensorflow\models\research\object_detection\data中找打一個叫pet_label_map.pbtxt的文件打開,
復制全部內容后,粘貼到hand_label_map.pbtxt中,並把name: ' ' 內的內容修改為需要修改的內容,
保存好,.pbtxt文件制作完成。
在D:\tensorflow\models\research\object_detection\dataset_tools中找到create_pascal_tf_record.py文件並打開,在165行更改成如下形式,和D:\tensorflow\hand_data\VOC2012\ImageSets\Main中的一個txt文件保持一致即可。
接下來,win+R→cmd→
C:\>cd /d D:\tensorflow\models\research D:\tensorflow\models\research>python object_detection/dataset_tools/create_pascal_tf_record.py --label_map_path=D:/tensorflow/hand_data/hand_label_map.pbtxt --data_dir=D:\tensorflow\hand_data --year=VOC2012 --set=train --output_path=D:\tensorflow\hand_set\data\pascal_train.record
注意:期間可能會遇到個報錯 TypeError:can’t pickle dict_values objects.
解決辦法:https://blog.csdn.net/sy20173081277/article/details/84311730
運行生成record文件,並把之前做好的hand_label_map.pbtxt文件一並放到該文件夾下。
⑤ 預訓練模型的選擇
打開https://github.com/tensorflow/models/tree/master/research/object_detection
找到model zoo,在這里面有各種各樣的模型。
我們選擇下面這個模型,從Speed中可以看到模型的速度。有興趣的也可以嘗試其他的模型。點擊下載到D:\tensorflow。
config文件的配置
在D:\tensorflow\models\research\object_detection\samples\configs中找到ssd_mobilenet_v1_pets.config文件。復制粘貼到D:\tensorflow\handset\model中。
打開ssd_mobilenet_v1_pets.config,把第9行的數字修改為需要分類的類別數
路徑修改(注意反斜杠和斜桿的問題):
fine_tune_checkpoint 后的路徑改為 ssd_mobilenet_v1_pets.config 的路徑;
train_input_reader下的 input_path 后的路徑改為 pascal_train.record 的路徑,label_map_path后的路徑改為 hand_label_map.pbtxt 的路徑;
eval_input_reader下的路徑同上。
模型訓練
首先在cmd中執行下面的命令
pip install git+https://github.com/philferriere/cocoapi.git#subdirectory=PythonAPI
##注:該命令時間稍長,建議等待;自己下載git文件,編譯會出現vc2014 等待系列依賴問題;我之前就是在這里栽了,各種惡心抓狂的開端;
然后在cmd中執行
C:\>cd /d D:\tensorflow\models\research
D:\tensorflow\models\research>python object_detection/model_main.py --pipeline_config_path=D:/tensorflow/hand_set/model/ssd_mobilenet_v1_pets.config --model_dir=D:\tensorflow\hand_set\model\train --num_train_steps=200 --num_eval_steps=5 --alsologtostderr
即可開始訓練,因為tensorflow是CPU版本的,所以訓練時間比較長。至少五到六個小時;
⑥ 訓練過程查看
C:\>cd\
C:\>tensorboard --logdir=D:\tensorflow\handset\model\train --host=127.0.0.1
得到http://127.0.0.1:6006,在瀏覽器中打開,即可看到過程參數,識別圖像等,都可以自己嘗試點擊看看。
⑦ 數據模型導出
在cmd中輸入
C:\>cd /d D:\tensorflow\models\research
D:\tensorflow\models\research>python object_detection/export_inference_graph.py --input_type=image_tensor --pipeline_config_path=D:\tensorflow\hand_set\model\ssd_mobilenet_v1_pets.config --trained_checkpoint_prefix=D:\tensorflow\hand_set\model\train\model.ckpt-200 --output_directory=D:\tensorflow\hand_set\export
即可導出數據,在D:\tensorflow\handset\export中可以看到導出的模型。
⑧ 實時手勢識別
# -*- coding: utf-8 -*- """ Created on Mon Mar 16 09:23:06 2020 @author: illusioned.zhy """ import os import sys import tarfile import cv2 as cv import numpy as np import tensorflow as tf from utils import label_map_util from utils import visualization_utils as vis_util # Path to frozen detection graph. This is the actual model that is used for the object detection. PATH_TO_FROZEN_GRAPH = 'D:/tensorflow/hand_set/export/frozen_inference_graph.pb' # List of the strings that is used to add correct label for each box. PATH_TO_LABELS = os.path.join('D:/tensorflow/hand_set/data', 'hand_label_map.pbtxt') NUM_CLASSES = 3 detection_graph = tf.Graph() capture = cv.VideoCapture(0) with detection_graph.as_default(): od_graph_def = tf.GraphDef() with tf.gfile.GFile(PATH_TO_FROZEN_GRAPH, 'rb') as fid: serialized_graph = fid.read() od_graph_def.ParseFromString(serialized_graph) tf.import_graph_def(od_graph_def, name='') label_map = label_map_util.load_labelmap(PATH_TO_LABELS) categorys = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=NUM_CLASSES, use_display_name=True) category_index = label_map_util.create_category_index(categorys) with detection_graph.as_default(): with tf.Session(graph=detection_graph) as sess: while True: ret, image = capture.read() if ret is True: image_np_expanded = np.expand_dims(image, axis=0) image_tensor = detection_graph.get_tensor_by_name('image_tensor:0') boxes = detection_graph.get_tensor_by_name('detection_boxes:0') scores = detection_graph.get_tensor_by_name('detection_scores:0') classes = detection_graph.get_tensor_by_name('detection_classes:0') num_detections = detection_graph.get_tensor_by_name('num_detections:0') (boxes,scores,classes,num_detections)=sess.run([boxes, scores, classes, num_detections], feed_dict={image_tensor: image_np_expanded}) vis_util.visualize_boxes_and_labels_on_image_array( image, np.squeeze(boxes), np.squeeze(classes).astype(np.int32), np.squeeze(scores), category_index, min_score_thresh=0.5, #置信度,默認為0.5 use_normalized_coordinates=True, line_thickness=4 ) c = cv.waitKey(5) if c == 27: # ESC break cv.imshow("Hand Gesture Demo", image) else: break cv.waitKey(0) cv.destoryAllWindows()
注意要把路徑寫對。
大功告成!
后面描述不清楚的地方,可以參考回原文 https://zhuanlan.zhihu.com/p/56699632
最后,附一張用20張圖片標注生成的圖片:
2020 疫情期間,帶着口罩上班;=_=!! 明明是V,這准確度和人工"智障"!!!自己把自己笑死~~~