辨識剪刀、石頭、布
這篇教學會使用 Teachable Machine 訓練「剪刀、石頭、布」的影像模型,再透過 OpenCV 搭配 tensorflow 讀取攝影鏡頭影像進行辨識。
快速導覽:
因為程式使用 Jupyter 搭配 Tensorflow 進行開發,所以請先閱讀「使用 Anaconda」和「Jupyter 安裝 Tensorflow」,安裝對應的套件。
安裝相關套件
因 Tensorflow 和 Python 版本更新的緣故 ( 原本 Jupyter 方式可能無法操作 ),建議參考「使用 Python 虛擬環境」,建立虛擬環境進行實作 ( 目前測試可運行的 Python 版本為 3.9.6 ),並使用下列指令安裝套件:
安裝 OpenCV,測試可運行版本為 4.9.0.80。
pip install opencv-python
安裝 Tensorflow,測試可運行版本為 2.15.0。
pip install tensorflow
Teachable Machine 建立分類,訓練模型
開啟 Teachable Machine 網站後,點擊「開始使用」建立新專案 ( 最下方可以切換語系為繁體中文 ),選擇 「圖片專案 > 標準圖片模型」,進入圖片模型訓練流程。
參考「使用 Teachable Machine」文章,訓練「剪刀」、「石頭」和「布」以及「背景」共四個分類,範例使用 a 作為剪刀分類的名稱,b 作為石頭分類名稱,c 作為布分類名稱,bg 作為背景分類名稱 ( 建議左手和右手都要訓練 )。
為什麼要訓練「背景」呢?因為如果沒有背景分類,會在沒有出拳的時候自動判斷最接近的分類,導致判斷出錯,所以建議一定要訓練一個背景的分類 ( 背景的分類除了單純的背景,也可以加入許多雜亂的圖片,增加背景的準確度 )。
分類建立後點擊「訓練模型」,等待訓練完成,可以從預覽區域測試模型。
確認辨識結果沒問題,就可以將模型匯出為 OpenCV Keras 類型,解壓縮後將 .h5 的模型檔案放到指定的資料夾裡。
搭配 OpenCV 進行辨識,並加入文字
下方的程式碼使用 OpenCV 讀取攝影鏡頭的影像,即時判斷現在出現的影像是什麼分類,並透過 putText() 方法,在影像中加入分類的名稱。
from keras.models import load_model # TensorFlow is required for Keras to work
import cv2 # Install opencv-python
import numpy as np
# Disable scientific notation for clarity
np.set_printoptions(suppress=True)
# Load the model
model = load_model("keras_Model.h5", compile=False)
def text(text): # 建立顯示文字的函式
global show_img # 設定 img 為全域變數
org = (0,50) # 文字位置
fontFace = cv2.FONT_HERSHEY_SIMPLEX # 文字字型
fontScale = 2.5 # 文字尺寸
color = (255,255,255) # 顏色
thickness = 5 # 文字外框線條粗細
lineType = cv2.LINE_AA # 外框線條樣式
cv2.putText(show_img, text, org, fontFace, fontScale, color, thickness, lineType) # 放入文字
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Cannot open camera")
exit()
while True:
ret, frame = cap.read()
if not ret:
print("Cannot receive frame")
break
img = cv2.resize(frame , (398, 224))
show_img = img[0:224, 80:304]
img = np.asarray(show_img, dtype=np.float32).reshape(1, 224, 224, 3)
img = (img / 127.5) - 1
prediction = model.predict(img)
index = np.argmax(prediction)
print(index)
if index == 0:
text('a') # 使用 text() 函式,顯示文字
if index == 1:
text('b')
if index == 2:
text('c')
cv2.imshow("Webcam Image", show_img)
if cv2.waitKey(1) == ord('q'):
break # 按下 q 鍵停止
cap.release()
cv2.destroyAllWindows()
如果要加入中文,必須要使用 pillow 函式庫,開啟命令提示字元或終端機,啟動 tensorflow 虛擬環境安裝 pillow 函式庫,再使用下方的程式碼,就能夠加入中文的文字。
from keras.models import load_model # TensorFlow is required for Keras to work
import cv2 # Install opencv-python
import numpy as np
from PIL import ImageFont, ImageDraw, Image # 載入 PIL 相關函式庫
fontpath = 'NotoSansTC-Regular.ttf' # 設定字型路徑
# Disable scientific notation for clarity
np.set_printoptions(suppress=True)
# Load the model
model = load_model("keras_Model.h5", compile=False)
def text(text): # 建立顯示文字的函式
global show_img # 設定 img 為全域變數
org = (0,50) # 文字位置
font = ImageFont.truetype(fontpath, 50) # 設定字型與文字大小
imgPil = Image.fromarray(show_img) # 將 img 轉換成 PIL 影像
draw = ImageDraw.Draw(imgPil) # 準備開始畫畫
draw.text((0, 0), text, fill=(255, 255, 255), font=font) # 寫入文字
show_img = np.array(imgPil)
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Cannot open camera")
exit()
while True:
ret, frame = cap.read()
if not ret:
print("Cannot receive frame")
break
img = cv2.resize(frame , (398, 224))
show_img = img[0:224, 80:304]
img = np.asarray(show_img, dtype=np.float32).reshape(1, 224, 224, 3)
img = (img / 127.5) - 1
prediction = model.predict(img)
index = np.argmax(prediction)
print(index)
if index == 0:
text('布') # 使用 text() 函式,顯示文字
if index == 1:
text('剪刀')
if index == 2:
text('石頭')
cv2.imshow("Webcam Image", show_img)
if cv2.waitKey(1) == ord('q'):
break # 按下 q 鍵停止
cap.release()
cv2.destroyAllWindows()
意見回饋
如果有任何建議或問題,可傳送「意見表單」給我,謝謝~