使用Tensorflow+OpenCV構建會玩石頭剪刀布的AI

使用Tensorflow+OpenCV構建會玩石頭剪刀布的AI

這個項目的代碼能夠在個人Github上找到python

  • https://github.com/HOD101s/RockPaperScissor-AI-
    簡介
    這個項目的基礎是深度學習和圖像分類,目的是建立一個簡單而有趣的石頭剪刀布遊戲。首先,這個項目是我在5月份的COVID19隔離期中無聊的產物,但願當你讀到這個時,一切都恢復正常了。個人目的是經過這篇文章用簡單的術語向初學者解釋這個項目的基本原理。讓咱們開始吧!
    在構建任何類型的深度學習應用程序時,有三個主要步驟:
  • 收集和處理數據
  • 創建一個合適的人工智能模型
  • 部署使用
    整個項目都引用了個人Github repo,並與之攜手並進,因此請作好參考準備。
    項目地址:https://github.com/HOD101s/RockPaperScissor-AI-
    收集咱們的數據
    使用Tensorflow+OpenCV構建會玩石頭剪刀布的AI
    任何深度學習模型的基礎都是數據,任何一位機器學習工程師都會贊成這一點,在ML中,數據遠比算法自己重要。咱們須要收集石頭,布和剪刀的符號圖像,我沒有下載別人的數據並在上面進行訓練,而是製做了本身的數據集,鼓勵你也創建本身的數據集。以後嘗試更改數據並從新訓練模型,以查看數據對深度學習模型究竟有怎樣的影響。
PATH = os.getcwd()+'\\'
cap = cv2.VideoCapture(0)

label = sys.argv[1]

SAVE_PATH = os.path.join(PATH, label)

try:
    os.mkdir(SAVE_PATH)
except FileExistsError:
    pass

ct = int(sys.argv[2])
maxCt = int(sys.argv[3])+1
print("Hit Space to Capture Image")

while True:
    ret, frame = cap.read()
    cv2.imshow('Get Data : '+label,frame[50:350,100:450])
    if cv2.waitKey(1) & 0xFF == ord(' '):
        cv2.imwrite(SAVE_PATH+'\\'+label+'{}.jpg'.format(ct),frame[50:350,100:450])
        print(SAVE_PATH+'\\'+label+'{}.jpg Captured'.format(ct))
        ct+=1
    if ct >= maxCt:
        break

cap.release()
cv2.destroyAllWindows()

我使用了Python的OpenCV庫進行全部與相機相關的操做,因此這裏的label指的是圖像屬於哪一個類,根據標籤,圖像保存在適當的目錄中。ct和maxCt是用來保存圖像的起始索引和最終索引,剩下的是標準的OpenCV代碼,用於獲取網絡攝像頭源並將圖像保存到目錄中。須要注意的一點是,我全部的圖片維數都是300 x 300的。運行此目錄樹後,個人目錄樹以下所示。git

C:.
├───paper
    │ paper0.jpg
    │ paper1.jpg
    │ paper2.jpg
│
├───rock
    │ rock0.jpg
    │ rock1.jpg
    │ rock2.jpg
│
└───scissor
     scissor0.jpg
     scissor1.jpg
     scissor2.jpg

若是你引用的是Github存儲庫(https://github.com/HOD101s/RockPaperScissor-AI-) ,則getData.py會爲你完成這項工做!
預處理咱們的數據
咱們須要使用圖像,而計算機能夠識別數字,所以,咱們將全部圖像轉換爲它們各自的矢量表示,另外,咱們的標籤尚待生成,因爲已創建的標籤不能是文本,所以我使用shape_to_label字典爲每一個類手動構建了「獨熱編碼」表示。github

DATA_PATH = sys.argv[1] # Path to folder containing data

shape_to_label = {'rock':np.array([1.,0.,0.,0.]),'paper':np.array([0.,1.,0.,0.]),'scissor':np.array([0.,0.,1.,0.]),'ok':np.array([0.,0.,0.,1.])}
arr_to_shape = {np.argmax(shape_to_label[x]):x for x in shape_to_label.keys()}

imgData = list()
labels = list()

for dr in os.listdir(DATA_PATH):
    if dr not in ['rock','paper','scissor']:
        continue
    print(dr)
    lb = shape_to_label[dr]
    i = 0
    for pic in os.listdir(os.path.join(DATA_PATH,dr)):
        path = os.path.join(DATA_PATH,dr+'/'+pic)
        img = cv2.imread(path)
        imgData.append([img,lb])
        imgData.append([cv2.flip(img, 1),lb]) #horizontally flipped image
        imgData.append([cv2.resize(img[50:250,50:250],(300,300)),lb]) # zoom : crop in and resize
        i+=3
    print(i)

np.random.shuffle(imgData)

imgData,labels = zip(*imgData)

imgData = np.array(imgData)
labels = np.array(labels)

當咱們根據類將圖像保存在目錄中時,目錄名用做標籤,該標籤使用shape_to_label字典轉換爲獨熱表示。在咱們遍歷系統中的文件以訪問圖像以後,cv2.imread()函數返回圖像的矢量表示。
咱們經過翻轉圖像並放大圖像來進行一些手動的數據加強,這增長了咱們的數據集大小,而無需拍攝新照片,數據加強是生成數據集的關鍵部分。最後,圖像和標籤存儲在單獨的numpy數組中。算法

  • cv2.imread()函數
    https://www.geeksforgeeks.org/python-opencv-cv2-imread-method/
  • 更多關於數據加強的信息。
    https://towardsdatascience.com/data-augmentation-for-deep-learning-4fe21d1a4eb9
    經過遷移學習創建咱們的模型:
    在處理圖像數據時,有許多通過預訓練的模型可供使用,這些模型已經在具備數千個標籤的數據集上進行了訓練,因爲這些模型經過其應用程序api的Tensorflow和Keras分佈,咱們可使用這些模型,這使得在咱們的應用程序中包含這些預先訓練的模型看起來很容易!
    使用Tensorflow+OpenCV構建會玩石頭剪刀布的AI
    總之,遷移學習採用的是通過預訓練的模型,而且不包含進行最終預測的最終層,可以區分這種狀況下圖像中的特徵,並將這些信息傳遞給咱們本身的Dense神經網絡。
    爲何不訓練你本身的模型呢?徹底取決於你!然而,使用遷移學習能夠在不少時候使你的進步更快,從某種意義上說,你避免了重複造輪子。
    其餘一些受歡迎的預訓練模型:
  • InceptionV3
  • VGG16/19
  • ResNet
  • MobileNet
    這是一篇關於遷移學習的有趣文章!
  • https://ruder.io/transfer-learning/
    注:每當咱們處理圖像數據時,幾乎都會使用卷積神經層,這裏使用的遷移學習模型就有這些層。有關CNNs的更多信息,請訪問:
  • https://medium.com/@RaghavPrabhu/understanding-of-convolutional-neural-network-cnn-deep-learning-99760835f148
    實現
    使用Tensorflow+OpenCV構建會玩石頭剪刀布的AI
    我已經使用DenseNet121模型進行特徵提取,其輸出最終將輸入到我本身的Dense神經網絡中。
densenet = DenseNet121(include_top=False, weights='imagenet', classes=3,input_shape=(300,300,3))
densenet.trainable=True

def genericModel(base):
    model = Sequential()
    model.add(base)
    model.add(MaxPool2D())
    model.add(Flatten())
    model.add(Dense(3,activation='softmax'))
    model.compile(optimizer=Adam(),loss='categorical_crossentropy',metrics=['acc'])
    return model

dnet = genericModel(densenet)

history = dnet.fit(
    x=imgData,
    y=labels,
    batch_size = 16,
    epochs=8,
    callbacks=[checkpoint,es],
    validation_split=0.2
)

關鍵點 :json

  • 因爲咱們的圖片尺寸爲300x300,所以指定的輸入形狀也爲3x300x300,3表明RGB的維度信息,所以該層具備足夠的神經元來處理整個圖像。
  • 咱們將DenseNet層用做第一層,而後使用咱們本身的Dense神經網絡。
  • 我已將可訓練參數設置爲True,這也會從新訓練DenseNet的權重。儘管花了不少時間,可是這給了我更好的結果。我建議你在本身的實現中嘗試經過更改此類參數(也稱爲超參數)來嘗試不一樣的迭代。
  • 因爲咱們有3類Rock-Paper-Scissor,最後一層是具備3個神經元和softmax激活的全鏈接層。
  • 最後一層返回圖像屬於3類中特定類的機率。
  • 若是你引用的是GitHub repo(https://github.com/HOD101s/RockPaperScissor-AI-) 的train.py,則要注意數據準備和模型訓練!
    至此,咱們已經收集了數據,創建並訓練了模型,剩下的部分是使用OpenCV進行部署
    OpenCV實現:
    此實現的流程很簡單:
  • 啓動網絡攝像頭並讀取每一個幀
  • 將此框架傳遞給模型進行分類,即預測類
  • 用電腦隨意移動
  • 計算分數
def prepImg(pth):
    return cv2.resize(pth,(300,300)).reshape(1,300,300,3)

with open('model.json', 'r') as f:
    loaded_model_json = f.read()
loaded_model = model_from_json(loaded_model_json)
loaded_model.load_weights("modelweights.h5")
print("Loaded model from disk")

for rounds in range(NUM_ROUNDS):
    pred = ""
    for i in range(90):
        ret,frame = cap.read()

        # Countdown    
        if i//20 < 3 :
            frame = cv2.putText(frame,str(i//20+1),(320,100),cv2.FONT_HERSHEY_SIMPLEX,3,(250,250,0),2,cv2.LINE_AA)

        # Prediction
        elif i/20 < 3.5:
            pred = arr_to_shape[np.argmax(loaded_model.predict(prepImg(frame[50:350,100:400])))]

        # Get Bots Move
        elif i/20 == 3.5:
            bplay = random.choice(options)            
            print(pred,bplay)

        # Update Score
        elif i//20 == 4:
            playerScore,botScore = updateScore(pred,bplay,playerScore,botScore)
            break

        cv2.rectangle(frame, (100, 150), (300, 350), (255, 255, 255), 2)
        frame = cv2.putText(frame,"Player : {}      Bot : {}".format(playerScore,botScore),(120,400),cv2.FONT_HERSHEY_SIMPLEX,1,(250,250,0),2,cv2.LINE_AA)
        frame = cv2.putText(frame,pred,(150,140),cv2.FONT_HERSHEY_SIMPLEX,1,(250,250,0),2,cv2.LINE_AA)
        frame = cv2.putText(frame,"Bot Played : {}".format(bplay),(300,140),cv2.FONT_HERSHEY_SIMPLEX,1,(250,250,0),2,cv2.LINE_AA)        
        cv2.imshow('Rock Paper Scissor',frame)
        if cv2.waitKey(1) & 0xff == ord('q'):
            break

上面的代碼片斷包含至關重要的代碼塊,其他部分只是使遊戲易於使用,RPS規則和得分。
因此咱們開始加載咱們訓練過的模型,它在開始程序的預測部分以前顯示倒計時,預測後,分數會根據球員的動做進行更新。
使用Tensorflow+OpenCV構建會玩石頭剪刀布的AI
咱們使用cv2.rectangle()顯式地繪製目標區域,使用prepImg()函數預處理後,只有幀的這一部分傳遞給模型進行預測。
整個play.py在個人repo上有代碼(https://github.com/HOD101s/RockPaperScissor-AI-/blob/master/play.py)。
結論:
咱們已經成功地實現並學習了這個項目的工做原理,因此請繼續使用個人實現進行其它實驗學習。我作的一個主要的改進多是增長了手部檢測,因此咱們不須要顯式地繪製目標區域,模型將首先檢測手部位置,而後進行預測。我鼓勵你改進這個項目,並給我你的建議。精益求精!
原文連接:https://towardsdatascience.com/building-a-rock-paper-scissors-ai-using-tensorflow-and-opencv-d5fc44fc8222api

相關文章
相關標籤/搜索