# 期末專案 2
## Virtual Calculator
### 安裝套件
進入文件設置,點擊==Project: pyramidProject==→==Python Interpreter==
選擇添加==cvzone==、==mediapipe==


### 導入套件
從cvzone導入手部追蹤模塊中的手部檢測器
==HandDetector==用來檢測我們的手是否點擊了按鈕
```python=
import cv2
from cvzone.HandTrackingModule import HandDetector
```
### 設置鏡頭
指定所要使用的攝影機,並寫一個循環使相機不斷捕捉影像,最後輸出影像
```python=
# 指定使用相機
cap = cv2.VideoCapture(0)
# 不斷地使用相機,並補抓影像
while True:
# success 中會指定有沒有成功地抓到資料, True 就是成功,False 就是失敗
# img 如果成功,那 img 中就是目前攝影機的影像
success, img = cap.read()
# 將圖片做水平翻轉
img = cv2.flip(img, 1)
# 顯示圖像
cv2.imshow("Image", img)
# 延遲一毫秒的時間
cv2.waitKey(1)
```
### 檢測手指
先做初始化設定,並檢測手部
```python=
import cv2
from cvzone.HandTrackingModule import HandDetector
# 指定使用相機
cap = cv2.VideoCapture(0)
# 產生一個手部骨架偵測器,將信心度設為0.8,並且只偵測一隻手
detector = HandDetector(detectionCon=0.8, maxHands=1)
#不斷地使用相機,並補抓影像
while True:
# success 中會指定有沒有成功地抓到資料, True 就是成功,False 就是失敗
# img 如果成功,那 img 中就是目前攝影機的影像
success, img = cap.read()
# 將圖片做水平翻轉
img = cv2.flip(img, 1)
# 將攝影機所擷取的圖片傳入,由圖片中找出手的骨架
# 回傳值的 hands 就是骨架的資訊
# img 則是 detector 會在圖片中畫出手的骨架,之後回傳
hands, img = detector.findHands(img,flipType=False)
# 顯示圖像
cv2.imshow("Image", img)
# 延遲一毫秒的時間
cv2.waitKey(1)
```
### 初始化按鈕
```python=
#定義一個名為 Button() 的類,並提供位置、寬度和長度作為輸入
#以便我們可以按照明確定義的順序排列鍵盤按鍵
class Button:
def __init__(self, pos, width, height, value):
self.pos = pos
self.width = width
self.height = height
self.value = value
#初始化按鈕,並定義一個名為draw()的函數,它接受兩個引數,即影象和按鈕列表並返回影象
def draw(self, img):
cv2.rectangle(img, self.pos, (self.pos[0] + self.width, self.pos[1] + self.height),
(225, 225, 225), cv2.FILLED)
cv2.rectangle(img, self.pos, (self.pos[0] + self.width, self.pos[1] + self.height),
(50, 50, 50), 3)
cv2.putText(img, self.value, (self.pos[0] + 30, self.pos[1] + 70), cv2.FONT_HERSHEY_PLAIN,
2, (50, 50, 50), 2)
```
### 創建按鈕
```python=
#給出位置輸入附加在一個名為 button list 的列表中
#我們可以將這個列表傳遞給 draw 函數以在我們的實時框架之上進行繪製
buttonList = []
for x in range(4):
for y in range(4):
xpos = x * 100 + 800
ypos = y * 100 + 150
buttonList.append(Button((xpos, ypos), 100, 100, '5'))
```
畫出所有按鈕,並放在 while 迴圈中
```python=
while True:
# success 中會指定有沒有成功地抓到資料, True 就是成功,False 就是失敗
# img 如果成功,那 img 中就是目前攝影機的影像
success, img = cap.read()
# 將圖片做水平翻轉
img = cv2.flip(img, 1)
# 將攝影機所擷取的圖片傳入,由圖片中找出手的骨架
# 回傳值的 hands 就是骨架的資訊
# img 則是 detector 會在圖片中畫出手的骨架,之後回傳
hands, img = detector.findHands(img,flipType=False)
#畫出所有按鈕
for button in buttonList:
button.draw(img)
# 顯示圖像
cv2.imshow("Image", img)
# 延遲一毫秒的時間
cv2.waitKey(1)
```
如下圖所示

### 按鈕列表值
```python=
#更改列表內的植
buttonListValues = [['7', '8', '9', '*'],
['4', '5', '6', '-'],
['1', '2', '3', '+'],
['0', '/', '.', '=']]
buttonList = []
for x in range(4):
for y in range(4):
xpos = x * 100 + 800
ypos = y * 100 + 150
#更改動態值設定
buttonList.append(Button((xpos, ypos), 100, 100, buttonListValues[y][x]))
```

### 新增上方計算列
```python=
#新增上方計算列,調整大小及位置
cv2.rectangle(img, (800, 50), (800 + 400, 70 + 100),
(225, 225, 225), cv2.FILLED)
cv2.rectangle(img, (800, 50), (800 + 400, 70 + 100),
(50, 50, 50), 3)
for button in buttonList:
button.draw(img)
```

### 加入變量
```python=
myEquation = ''
delayCounter = 0
```
#### 找到手指間的距離,判斷是否點擊按鈕
```python=
if hands:
lmList = hands[0]['lmList']
#依照食指尖及中指尖的距離來計算是否單擊或沒有
length, _, img = detector.findDistance(lmList[8], lmList[12], img)
print(length)
#將食指定為指標
x, y = lmList[8]
# 單擊檢查哪個按鈕並執行操作
if length < 50 and delayCounter == 0:
for i, button in enumerate(buttonList):
if button.checkClick(x, y):
myValue = buttonListValues[int(i % 4)][int(i / 4)]
# 得到正確的數字
if myValue == '=':
myEquation = str(eval(myEquation))
else:
myEquation += myValue
delayCounter = 1
```
#### 檢查是否已點擊
```python=
#檢查X、Y初始值與寬度、高度和顏色
def checkClick(self, x, y):
if self.pos[0] < x < self.pos[0] + self.width and \
self.pos[1] < y < self.pos[1] + self.height:
cv2.rectangle(img, (self.pos[0] + 3, self.pos[1] + 3),
(self.pos[0] + self.width - 3, self.pos[1] + self.
height - 3),(255, 255, 255), cv2.FILLED)
cv2.putText(img, self.value, (self.pos[0] + 25,
self.pos[1] + 80), cv2.FONT_HERSHEY_PLAIN,5, (0, 0, 0), 5)
return True
else:
return False
```
#### 顯示結果
```python=
cv2.putText(img, myEquation, (810, 120), cv2.FONT_HERSHEY_PLAIN,
3, (0, 0, 0), 3)
```

### 延遲設定
```python=
# to avoid multiple clicks
#設定延遲計數器和計數器
if delayCounter != 0:
delayCounter += 1
if delayCounter > 10:
delayCounter = 0
```
```python=
# 顯示圖像
key = cv2.waitKey(1)
cv2.imshow("Image", img)
if key == ord('c'):
myEquation = ''
```
[Code](https://www.computervision.zone/lessons/code-and-files-9)
[Video](https://www.youtube.com/watch?v=DZMJ77akgec&t=3536s)