使用 python3+OpenCV+TensorFlow 做手勢識別


本文很大程度的參考了知乎大大的文章:

<使用 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,這准確度和人工"智障"!!!自己把自己笑死~~~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM