# Basketball Shot Predictor 投籃預測器
--------------------------------
組員:C107143123李旻穎、C107143128童品澄、C107143130林譁恩
## 壹、程式目標
此程式目標是要透過資料集所提供的射籃影片,追蹤影片中籃球的射程,並根據其初始移動方向預測籃球是否會成功落入籃框內,並能成功顯示出預測結果。
如下圖所示
<font color='GREEN'>綠點</font>:籃球射程所經位置
<font color='PURPLE'>紫線</font>:透過綠點所得出籃球射程預測曲線

--------------------------------
## 貳、程式說明
利用多項式回歸中一元二次方程式預測籃球移動路徑。
運行環境:使用公開CVZONE與自行設定之cvzone
程式架構如下:
- 辨識影片中籃球顏色
- 透過成功辨識顏色框出籃球輪廓
- 辨識籃球輪廓框之中心點
- 將每個辨識到的籃球輪廓框中心點都相連成線獲得籃球所經路徑
- 預測籃球未來路徑
- 根據預測未來路徑顯示籃球是否能成功入籃框,若預測其將成功入框就讓畫面顯示Basket,失敗就顯示 No Basket
### 環境設定
- 下載PCCHARM
- 新建檔案(檔名為Basketball Predicter)
- 點選File(檔案)
- 點選 Setting (設定):下載所有套件
- 點選Python Interpreter ,此時會顯示<font color='grey'> <No Interpreter> </font>
- 點選 <No Interpreter> 旁邊的設定圖示,如下圖1所示

- 點選新增ADD
- 點選已存在之設定環境Existing Environment 並確定ok
--------------------------------
### CVZONE套件設置
如下圖2所示,點選+號新增套件

- 跳出可運用套件頁面,搜尋cvzone
- 點選安裝Install
- 此CVZONE會安裝大部分的必要套件,包含opencv和NumPy以及數學運算式
>上述步驟完成,回到首頁,即可開始撰寫程式
### 新建檔案
新增檔案:點選Basketball Predicter按右鍵
檔名設為:main.pi
--------------------------------
## 參、程式撰寫
- 第一步:輸入套件
```python=
import cv2
import cvzone
```
- 第二步:測試用資料集影片載入
> 測試用資料集影片可從CVZONE官網下載,需先註冊並登入
>> 資料集影片網址:https://www.computervision.zone/lessons/code-and-files-11/
>>>點選<font color='red'> Click Here </font>, 如下圖,即可下載獲得資料影片

> 點選Paste,如下圖

將一張照片Ball.png與其他資料集中7個影片檔都以相同方式載入

------------------------------
### 初始化設定影片
>以資料集中的影片1舉例說明
程式碼如下:
```python=
cap = cv2.VideoCapture('Videos/vid (1).mp4')
```
>上述程式碼中之(1)是影片檔名之數字號碼*
```python=
while True:
success, img = cap.read()
cv2.imshow("Image", img)
cv2.waitkey=(1)
```
> 此步驟完成後可以嘗試運行,會發現此時出現兩個問題
> - 影片速度太快,來不及辨識
> - 影片畫面過大,不合螢幕
此時可先修正程式為
```python=
while True:
success, img = cap.read()
img = cv2.resize(img,(0,0),None,0.7)
cv2.imshow("Image", img)
cv2.waitkey=(50)
```
>- 0.7是估計值,可自行設定想讓影片縮小放大多少比例
>- waitkey修正為50亦為估計值,目的是減緩影片速度,幫助計算機辨識籃球所在位置
---
為了要確認<font color='orange'> 籃球所在位置 </font> ,我們需要用到Ball.img 這張圖像幫助辨識,因此要再將程式修改,如下:
```python=
while True:
# 擷取圖像
# success, img = cap.read()
img = cv2.dotimread("Ball.png")
img=[0:900]
img = cv2.resize(img,(0,0),None,0.7)
cv2.imshow("Image", img)
cv2.waitkey=(50)
```
>- 第4行程式碼:讀取橘色籃球的顏色幫助辨識
>- CROP切除下半部區域,由於Ball.img中,圖片底下棕色牆壁區塊跟橘色籃球之間的些微的相似性(如下圖所示),可能會影響計算機辨識,因此我們切除時,要將避免選到棕色牆壁區塊
> - 第5行程式碼:計算機以矩形辨識,全圖尺寸為1300x1080,我們只需切除高度而非寬度,因此切後除大小為y座標0-900,而x座標不需更改。

我們選擇用CVZONE套件來幫助辨識
所以程式碼修改如下
```python=
import cv2
import cvzone
from cvzone.ColorModule import ColorFinder
# 初始化影片
cap = cv2.VideoCapture('Videos/vid (4).mp4')
# 創建辨識像素顏色工具
myColorFinder = ColorFinder(False)
hsvVals = red
```
> - 在ColorFinder的()中我們可以選擇True或是Fasle,如果選擇False 將無法進行Debug,選擇True將會以Debug方式運行
> - myColorFinder為自訂名稱
> - 此時我們還不知道籃球顏色的參數,因此先以"red"代替,還需要另外處理,下面會做說明
下面是辨識籃球顏色所需的程式
> - 此時我們會得到影像像素顏色以及設定遮罩圖片的屬性
> - 總共需要兩個位置參數,但我們會提供三個
```python=
imgColor, mask = myColorFinder.update(img, hsvVals)
```
我們運行程式後會出現參數軌,如下圖

此時我們需要將參數軌調整到影片畫面中,除了我們想要的籃球顏色以外,其餘都是黑色的狀態,如下圖所示。

> 稍微有點不完整的部分可視為雜訊,瑕不掩瑜,只要範圍不大,不至於影響程式運行
這時只要打開主控台(console),就可以看到顏色參數,只要複製貼上在程式碼hsvVals = red的"red"的裡面更改即可

到這步為止,已經可以獲得辨識籃球像素顏色工具了
如下是完整程式碼
```python=
myColorFinder = ColorFinder(False)
hsvVals = {'hmin': 8, 'smin': 96, 'vmin': 115, 'hmax': 14, 'smax': 255, 'vmax': 255}
```
而辨識像素顏色需要用到下面程式
```python=
# 展示圖片像素顏色
imgContours = cv2.resize(imgContours, (0, 0), None, 0.7, 0.7)
```
---
### 辨識籃球輪廓
首先要找出最小範圍來降低雜訊干擾
```python=
imgContours, contours = cvzone.findContours(img, mask, minArea=500)
```
>- 500是指500像素
>- 此時可以運行程式,會看到程式成功將我們要的顏色標示成框,如下圖所示

---
### 辨識籃球運行軌跡
在這段程式中,我們要將每一個被上段程式中所辨識的最大輪廓框所經過的中心點都畫上一個記號。
這些記號相連便是籃球的運行軌跡。程式如下:
```python=
if contours:
posListX.append(contours[0]['center'][0])
posListY.append(contours[0]['center'][1])
```
接續延伸,底下說明
```python=
if posListX:
# Polynomial Regression y = Ax^2 + Bx + C
# Find the Coefficients
A, B, C = np.polyfit(posListX, posListY, 2)
for i, (posX, posY) in enumerate(zip(posListX, posListY)):
pos = (posX, posY)
cv2.circle(imgContours, pos, 10, (0, 255, 0), cv2.FILLED)
if i == 0:
cv2.line(imgContours, pos, pos, (0, 255, 0), 5)
else:
cv2.line(imgContours, pos, (posListX[i - 1], posListY[i - 1]), (0, 255, 0), 5)
```
>enumerate是為了看出他的迭代次數
>postList[i-1] 是為了要將每一個點i的前一個點i-1相連起來,以獲得曲線
此時運行程式,應可得如下圖之影片

> 可以看出已將籃球所經之點相連成曲線
---
### 預測籃球路徑
在這一步中,我們需要用到多項式回歸(Polynomial regression),也就是y = Ax^2 + Bx + C。這時需嵌入NumPy,是Python語言的一個擴充程式庫。NumPy支援高階大量的維度陣列與矩陣運算,此外也針對陣列運算提供大量的數學函式函式庫。
因此在最前面的套件設定,我們要加上下列程式碼
```python=
import numpy as np
```
接下來我們要將程式修改讓參數符合多項式回歸。如下所示
```python=
# Polynomial Regression y = Ax^2 + Bx + C
A, B, C = np.polyfit(posListX, posListY, 2)
for i, (posX, posY) in enumerate(zip(posListX, posListY)):
```
> - 數值2 為次方數,由於我們要的必須符合一元二次方程式的平面,因此數值為2,如果要預測的是立體圖形,那數值就改為3,以此類推。
> - zip是為了將x和y都一起讓迴圈運行
---
### 籃球畫面變項
```python=
posListX, posListY = [], []
xList = [item for item in range(0, 1300)]
prediction = False
```
> - 資料集影片提供的大小是1300x1300,所以我們讓辨識範圍在這之內。
---
### 籃球路徑預測線
```python=
for x in xList:
y = int(A * x ** 2 + B * x + C)
cv2.circle(imgContours, (x, y), 2, (255, 0, 255), cv2.FILLED)
```
>此內容中之數值2代表線的粗細,後續接著的(255, 0, 255)可以讓預測線呈現紫色
此時運行程式,應已可看到預測線出現。如下圖所示。

### 預測籃球是否會入籃框
首先,我們需要先將Ball.png以小畫家打開,利用小畫家,找出籃框的xy值。如下圖所示,若籃球移動路徑其中有x值落在黑線跟黑線中間那段籃框,同時高度在紅線位置y,就會進入籃框。

> 由小畫家確認可以得到一個大概的數值 x=330到430之間、 y=590
我們把這個結果記錄在程式碼中。
```python=
# 預測籃球落點位置是否能進框
# X values 330 to 430 Y 590
a = A
b = B
c = C - 590
x = int((-b - math.sqrt(b ** 2 - (4 * a * c))) / (2 * a))
```
> x的值必須等於一元二次方程式,如下圖

接下來,如果預測籃球結果能進框,就要顯示Basket,不能則顯示No Basket,程式碼如下。
```python=
prediction = 330 < x < 430
if prediction:
cvzone.putTextRect(imgContours, "Basket", (50, 150),
scale=5, thickness=5, colorR=(0, 200, 0), offset=20)
else:
cvzone.putTextRect(imgContours, "No Basket", (50, 150),
scale=5, thickness=5, colorR=(0, 0, 200), offset=20)
```
最後再修改成如果畫面長度x小於10才進行預測,否則預測結果會無法改變。如下所示
```python=
if len(posListX) < 10:
# Prediction
# X values 330 to 430 Y 590
a = A
b = B
c = C - 590
x = int((-b - math.sqrt(b ** 2 - (4 * a * c))) / (2 * a))
prediction = 330 < x < 430
if prediction:
cvzone.putTextRect(imgContours, "Basket", (50, 150),
scale=5, thickness=5, colorR=(0, 200, 0), offset=20)
else:
cvzone.putTextRect(imgContours, "No Basket", (50, 150),
scale=5, thickness=5, colorR=(0, 0, 200), offset=20)
```
到這步為止,程式就完整了,下面可以看到整理過的完整程式碼。
---
## 肆、完整程式碼
```python=
import math
import cv2
import cvzone
from cvzone.ColorModule import ColorFinder
import numpy as np
# 初始化影片
cap = cv2.VideoCapture('Videos/vid (4).mp4')
# 尋找像素顏色工具
myColorFinder = ColorFinder(False)
hsvVals = {'hmin': 8, 'smin': 96, 'vmin': 115, 'hmax': 14, 'smax': 255, 'vmax': 255}
# Variables
posListX, posListY = [], []
xList = [item for item in range(0, 1300)]
prediction = False
while True:
# 擷取影像
success, img = cap.read()
# img = cv2.imread("Ball.png")
img = img[0:900, :]
# 辨識籃球
imgColor, mask = myColorFinder.update(img, hsvVals)
# 辨識籃球所經過點
imgContours, contours = cvzone.findContours(img, mask, minArea=500)
if contours:
posListX.append(contours[0]['center'][0])
posListY.append(contours[0]['center'][1])
if posListX:
# 預測籃球路徑:多項式回歸 y = Ax^2 + Bx + C
# 辨識參數
A, B, C = np.polyfit(posListX, posListY, 2)
for i, (posX, posY) in enumerate(
zip(posListX, posListY)):
pos = (posX, posY)
cv2.circle(imgContours, pos, 10, (0, 255, 0), cv2.FILLED)
if i == 0:
cv2.line(imgContours, pos, pos, (0, 255, 0), 5)
else:
cv2.line(imgContours, pos, (posListX[i - 1], posListY[i - 1]), (0, 255, 0), 5)
for x in xList:
y = int(A * x ** 2 + B * x + C)
cv2.circle(imgContours, (x, y), 2, (255, 0, 255), cv2.FILLED)
if len(posListX) < 10:
# 預測籃球落點位置是否能進框
# X values 330 to 430 Y 590
a = A
b = B
c = C - 590
x = int((-b - math.sqrt(b ** 2 - (4 * a * c))) / (2 * a))
prediction = 330 < x < 430
if prediction:
cvzone.putTextRect(imgContours, "Basket", (50, 150),
scale=5, thickness=5, colorR=(0, 200, 0), offset=20)
else:
cvzone.putTextRect(imgContours, "No Basket", (50, 150),
scale=5, thickness=5, colorR=(0, 0, 200), offset=20)
# 展示圖片像素顏色
imgContours = cv2.resize(imgContours, (0, 0), None, 0.7, 0.7)
# cv2.imshow("Image", img)
cv2.imshow("ImageColor", imgContours)
cv2.waitKey(100)
```
## 伍、教學影片
{%youtube PVtmK8LmYpA %}
## 陸、心得感想
### C107143123李旻穎
看完影片後讓不太擅長python的我們更加熟悉如何寫程式,一開始在設定運行環境時遇到了很大的問題,因為影片裡沒有太多講到關於pccharm環境設定的細節,因此在環境設的環節卡了很久,再經過研究一番後,也都順利解決,後面在編輯程式時,我們了解了很多關於python 的編輯技巧,獲益良多,也多虧這個專案讓我們復習大一學的程式,完成這個專案後,更是讓我們獲得很多成就感。
### C107143128童品澄
我們自己在實作這個專案時,有發現這個專案的困難點在於辨識籃球的資料集影片,我們在環境設定的步驟是第一個困難點,因為在原始影片中並沒有很詳細說明。選取籃球圖片顏色也是讓我們感到困難重重,幸好最終都能如願解決。整體而言,能夠完成這樣一個有趣的專案實屬難得的經驗累積。
### C107143130林譁恩
原本就不是很擅長程式的我在一開始幾乎什麼都不是很了解,後來因為有這個學習網站的關係讓我知道如果編輯程式還有設定環境也了解了很多關於python 的編輯技巧,獲益良多,也多虧隊友一起的努力才能讓專案順利的完成。