1. 程式人生 > >opencv 利用攝像頭來判斷石頭剪刀布的小遊戲

opencv 利用攝像頭來判斷石頭剪刀布的小遊戲

首先在此宣告本文程式碼參考  部落格:https://github.com/az666/opencv-game/blob/master/shitoujiandaobu.py

參考文獻:https://blog.csdn.net/JohinieLi/article/details/81041276

                 https://blog.csdn.net/qq_31531635/article/details/73325693

自己進行了知識點的補充 以及程式碼裡註釋的補充。

知識點:個人總結就是一句話 獲取

二值圖

涉及到影象預處理 這裡利用的是腐蝕膨脹的方法: 形態學處理的核心就是定義結構元素,在OpenCV-Python中,可以使用其自帶的getStructuringElement函式,也可以直接使用NumPy的ndarray來定義一個結構元素。 

腐蝕和膨脹是影象的形態學處理中最基本的操作,之後遇見的開操作和閉操作都是腐蝕和膨脹操作的結合運算。腐蝕和膨脹的應用非常廣泛,而且效果還很好:

腐蝕可以分割(isolate)獨立的影象元素,膨脹用於連線(join)相鄰的元素,這也是腐蝕和膨脹後圖像最直觀的展現
去噪:通過低尺寸結構元素的腐蝕操作很容易去掉分散的椒鹽噪聲點
影象輪廓提取:腐蝕操作
影象分割

腐蝕操作描述為:掃描影象的每一個畫素,用結構元素與其覆蓋的二值影象做“與”操作:如果都為1,結果影象的該畫素為1,否則為0。

膨脹操作描述為:掃描影象的每一個畫素,用結構元素與其覆蓋的二值影象做“與”操作:如果都為0,結果影象的該畫素為0,否則為1。


     程式碼中用到的函式是     Mat getStructuringElement(int shape, //核的形狀 0:矩形 1:十字交叉形 2: 橢圓

                                                                                      Size ksize,//核大小

                                                                                       Point anchor=Point(-1,-1) //核中心位置,預設位於形狀中心處 );

     這裡我在程式碼中已經寫出來了。

 

import cv2
import numpy
import time
import random
import os
def judge():
    # 構造一個3×3的結構元素
    # return 0 stone ,1 jiandao, 2 bu
    img = cv2.imread("wif.jpg", 0)
    element = cv2.getStructuringElement(cv2.MORPH_RECT, (11, 11))#矩形:MORPH_RECT  交叉形:MORPH_CROSS  橢圓形:MORPH_ELLIPSE
    dilate = cv2.dilate(img, element)
    erode = cv2.erode(img, element)
    # 將兩幅影象相減獲得邊,第一個引數是膨脹後的影象,第二個引數是腐蝕後的影象
    result = cv2.absdiff(dilate, erode);
    # 上面得到的結果是灰度圖,將其二值化以便更清楚的觀察結果
    retval, result = cv2.threshold(result, 40, 255, cv2.THRESH_BINARY);
    # 反色,即對二值圖每個畫素取反
    result = cv2.bitwise_not(result);
    """
python-opencv影象的位操作:影象與運算cv2.bitwise_and,影象或運算cv2.bitwise_or,影象非運算cv2.bitwise_not與影象異或運算cv2.bitwise_xor。
1)影象與運算-cv2.bitwise_and(src1, src2, dst=None, mask = None)
2)影象或運算-cv2.bitwise_or(src1, src2, dst=None, mask = None)
3)影象非運算-cv2.bitwise_not(src1, src2, dst=None, mask = None)
影象非運算的效果:一個二值圖,將黑色轉為白色,白色轉為黑色。
    """
    result = cv2.medianBlur(result, 23)
    #cv2.imshow("test",result)
    """
這裡介紹非線性過濾器——中值濾波器。
由於中值濾波器對消除椒鹽現象特別有用。所以我們使用第二篇教程中椒鹽函式先對影象進行處理,將處理結果作為示例圖片。
呼叫中值濾波器的方法與呼叫其他濾波器的方法類似,如下:
result = cv2.medianBlur(image,5)  
函式返回處理結果,第一個引數是待處理影象,
第二個引數是孔徑的尺寸,一個大於1的奇數。
比如這裡是5,中值濾波器就會使用5×5的範圍來計算。即對畫素的中心值及其5×5鄰域組成了一個數值集,對其進行處理計算,當前畫素被其中值替換掉。
    """
    a = []
    posi = []
    width = []
    count = 0
    area = 0
    #這個大神的計算方法,我一時也解析不出來,應該是依靠手勢面積來計算的
    for i in range(result.shape[1]):
        for j in range(result.shape[0]):
            if (result[j][i] == 0):
                area += 1
    for i in range(result.shape[1]):
        if (result[5 * result.shape[0] // 16][i] == 0 and result[5 * result.shape[0] // 16][i - 1] != 0):
            count += 1
            width.append(0)
            posi.append(i)
        if (result[5 * result.shape[0] // 16][i] == 0):
            width[count-1]+= 1    # 如果在這裡報錯,是因為背景問題,請讓手的背景儘量整潔
    #這裡是除錯用的程式碼可以註釋掉
    print ('the pic width is ',result.shape[1],'\n')
    for i in range(count):
        print ('the ',i,'th',' ','is')
        print ('width ',width[i] )
        print ('posi ',posi[i],'\n')
    print (count,'\n')
    print ('area is ',area,'\n')
    cv2.line(result,(0,5*result.shape[0]//16),(214,5*result.shape[0]//16),(0,0,0))
    cv2.namedWindow("fcuk")
    cv2.imshow("fcuk",result)
    cv2.waitKey(0)
    #這裡是除錯用的程式碼可以註釋掉
    # 判定時間
    width_length = 0
    width_jiandao = True
    for i in range(count):
        if width[i] > 45:
            # print 'bu1';
            return 2;
        if width[i] <= 20 or width[i] >= 40:
            width_jiandao = False
        width_length += width[i]
    if width_jiandao == True and count == 2:
        return 1;
    if (area < 8500):
        print ('shi tou')
        return 0;
    print("width_leng", width_length)
    if (width_length < 35):
        # 這個時候說明照片是偏下的,所以需要重新測定。
        a = []
        posi = []
        width = []
        count = 0
        for i in range(result.shape[1]):
            if (result[11 * result.shape[0] // 16][i] == 0 and result[11 * result.shape[0] // 16][i - 1] != 0):
                count += 1
                width.append(0)
                posi.append(i)
            if (result[11 * result.shape[0] // 16][i] == 0):
                width[count - 1] += 1
        """ 
        print 'the pic width is ',result.shape[1],'\n' 
        for i in range(count): 
            print 'the ',i,'th',' ','is'; 
            print 'width ',width[i] 
            print 'posi ',posi[i],'\n' 
        print count,'\n' 
        print 'area is ',area,'\n' 
        """
    width_length = 0
    width_jiandao = True
    for i in range(count):
        if width[i] > 45:
            print ('bu1')
            return 2;
        if width[i] <= 20 or width[i] >= 40:
            width_jiandao = False
        width_length += width[i]
    if width_jiandao == True and count == 2:
        return 1;
    if (area > 14000 or count >= 3):
        print ('bu2')
        return 2;
    if (width_length < 110):
        print ('jian dao')
        return 1;
    else:
        print('bu3')
        return 2;
def game():
    fuck = []
    fuck.append("石頭")
    fuck.append("剪刀")
    fuck.append("布")
    capture = cv2.VideoCapture(0)
    cv2.namedWindow("camera", 1)
    start_time = time.time()
    print("給你5秒的時間把手放到方框的位置\n")
    while (1):
        ha, img = capture.read()
        end_time = time.time()
        cv2.rectangle(img, (426, 0), (640, 250), (170, 170, 0))
        cv2.putText(img, str(int((5 - (end_time - start_time)))), (100, 100), cv2.FONT_HERSHEY_SIMPLEX, 2, 255)
        cv2.imshow("camera", img)
        if (end_time - start_time > 5):
            break
        if (cv2.waitKey(30) >= 0):
            break
    ha, img = capture.read()
    capture.release()
    cv2.imshow("camera", img)
    img = img[0:210, 426:640]
    cv2.imwrite("wif.jpg", img)
    p1 = judge()
    pc = random.randint(0, 2)
    # print p1,' ',pc,'\n'
    print("你出的是", fuck[p1], " 電腦出的是", fuck[pc], "\n")
    cv2.destroyAllWindows()
    if (p1 == pc):
        print("平局\n")
        return 0
    if ((p1 == 0 and pc == 1) or (p1 == 1 and pc == 2) or (p1 == 2 and pc == 0)):
        print('你贏了\n')
        return 1
    else:
        print('你輸了\n')
        return -1
def main():
    you_win = 0
    pc_win = 0
    print("這是通過攝像頭來玩的剪刀石頭布的遊戲,請輸入回車開始遊戲\n")
    #s = raw_input()
    while (1):
        print("比分(玩家:電腦) ", you_win, ":", pc_win, '\n')
        #s = raw_input()
        #  清空螢幕
        os.system('cls')
        ans = game()
        #比分(玩家:電腦)  0 : 0  下來的就是比分的計算
        if (ans == 1):
            you_win += 1
        elif (ans == -1):
            pc_win += 1
        print("為了減少誤判,請儘可能將手佔據儘可能大的框框")

main()

詳細程式碼獲取:https://download.csdn.net/download/qq_40979622/10715445

可以給我留言一起討論。或者[email protected]郵箱交流。