## Feature dection
SIFT尺度不變特徵轉換
影像中的局部特徵
1. 偵測
2. 描述
# 視訊分析
[TOC]
## week2_introduction
Digitization數位化:用3步驟,將float像素值數位化為數位影像
1. Sampling : 將時間軸離散化
2. **==Quantization 量化==** : 將振幅軸切割成有限的位階(level),將 **振幅軸離散化,浮點數變成整數**
3. Coding : 整數值編成二元碼
物體經由鏡片投影產生影像(連續的),

生成取樣影像(像素位置是離散的、色彩值是連續的),量化將色彩值轉為離散數值

取樣(產生離散的位置x,y)+量化(每個位置得到離散的色彩值0~255)=數位影像可表示為二維矩陣,矩陣中的每一個元素稱為 **像素 I(x,y)**
MxN矩陣>> x=0~M-1;y=0~N-1 有M個row + N個colum
**數位影像的品質:**
* 取樣決定空間解析度
* 量化決定色彩解析度
R,G,B = 分別為256 level(8 bit)
**數位影像的類別:由量化(bit)數決定**
* Binary 二值影像(1 bit):0 or 1
* Gray-level(1 byte = 8bit):0~255
* True-Color(3 byte):255^3=16.7M colors
* Indexed-color(log2n bit)索引全彩影像,用較少的bit表示彩色影像

---
## week3_Image_Read、Show、Write
* **```cv2.imread()```** 讀
同個目錄下'name.jpg';其他目錄:'檔案路徑';0:gray imge/1:color/-1:alpha channel
* **```cv2.imshow()```** 秀
* **```cv2.imwrite()```** 存
```p=
import cv2 //使用openCV
img=cv2.("pohot_name",-1) // -1:用灰階 讀入圖片(放在同目錄下)放到陣列img裡
cv2.imshow("wondows_name",img) //開視窗 秀圖片img
cv2.waitkey(0) //等待輸入的指令(字母)/延遲X秒後結束 0:無限秒
cv2.destroyAllWindows() //關閉所有視窗
```
**```cv2.waitKey(delay)```** 接受一個按鈕事件並返回按鈕的ASCII碼; ==delay=1000:延遲1秒== 0:延遲時間無窮大
**```cv2.destroyWindow(winname)```** 關閉某視窗
e.g.

>超過0~255範圍 會不自然
圖片的型態```img.shape``` :秀出圖像的長垂直、寬水平、色彩(灰階1 黑白2 彩色3)
```python!
print("color_img_shap",img.shap)
```

**```cv2.imwrite("outname.jpg",img)```** 存檔,搭配 ```cv2.waitkey(0)```使用-按某鍵存檔:
```p=
k=cv2.waitkey(0)
...
if k==ord('s') //ord('c')==char()一個字母 回傳ASCII碼
cv2.imwrite("outname.jpg",img)
```
### 單獨秀出某通道的圖像
Python openCV uses **BGR** mode 顯示各個通道的圖像
* **```b,g,r=cv2.split(img)```** 把3種通道分割開來
* **```img2=cv2.merge(b,g,r)```** 合併成3種通道(彩色影像)
* ```cv2.imgshow()```
先切割BGR的通道>>有三個 長*寬 的矩陣,**裡面分別放0~255的數字**
**單獨抓某個通道會是灰階圖片**
```python!
cv2.imshow('single_green_channel', g)
#display single channel (regard as gray-level image)
```
所以要把其他兩個通道清空(放0),再與某通道合併,才能呈現單獨某色的圖像
```python=
img = cv2.imread( "WIN_20230302_13_18_26_Pro.jpg")
b,g,r = cv2.split(img) #分割成3種通道b,g,r
if img is None:
sys.exit("could not read photo.")
cv2.imshow( "Example", img )
b[:]=0 #b矩陣清空放0
g[:]=0
r_color=cv2.merge((b,g,r))
k=cv2.waitKey( 0 )
if k==ord("s"):
cv2.imshow( "R_color",r_color )
k=cv2.waitKey( 0 )
if k==ord("a"):
cv2.destroyAllWindows( )
print("color_img_shap",img.shape)
cv2.destroyAllWindows( )
print("done.")
```
---
## week4_video
**```cap=cv2.VideoCapture(0)```** create攝影機物件object
* 0:預設第一隻 1:第二隻 2:第三隻
* 一幀一幀(frame by frame)的捕捉攝像頭
**```ret,frame=cap.read()```** 回傳兩個值(bool/numpy.array)
* ret:True正確取幀(沒有漏掉)或 False
* frame:下一張影像的矩陣
**```cap.release() ```** 釋放幀;關閉攝影機
```cap.isOpened()```檢查攝影機是否啟動
```cap.open()```開啟攝影機
```cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)```將影像轉成灰階
```python=
#HW22目的:用攝影機捕捉1.全彩視訊、2.紅色、3.藍綠交換
cap=cv2.VideoCapture(0)
while True: #在無窮迴圈裡一直秀圖片>>影片串流
ret,frame=cap.read()
if not ret: #如果是flase(不正常取幀)
print("can't recive frame")
break
cv2.imshow('frame',frame) #秀出全彩影片串流
b,g,r=cv2.split(frame) #分割三種通道
temp=b #把blue green通道交換
b=g
g=temp
exchange=cv2.merge((b,g,r)) #合併
cv2.imshow('exchange',exchange)
b[:]=0 #清空(秀紅色通道)
g[:]=0
r_color=cv2.merge((b,g,r))
cv2.imshow('R_color',r_color)
if cv2.waitKey(1)==ord('q'): #按Q結束
cap.release() #釋放幀
cv2.destroyAllWindows()
break
```
### 儲存影片
**```cv2.VideoWriter(output_name,int fourcc,fps,size,全彩:true or flase)```**
* 檔名
* 壓縮的方式compress the frames: **DIVX**(Windows建議使用) 、XVID..
* fps每秒幀數
* 寬width、高heigt ex.640*480
* true:color false:gray
```p=
fourcc= cv2.VideoWriter_fourcc('DIVX')// 設定儲存的格式
out=cv2.VideoWriter(output_name,fourcc,20.0,(680,480))
```
### Numpy

```
x=np.array([ [1,2,3],[4,5,6] ])
```
#### 陣列屬性:
* np.ndim維度
* np.shap外型 #(row,column)
* np.size大小
* np.dtype資料內容:(unit8)
#### 陣列+-* /運算: **element-wise:*/是一個元素對一個元素運算**
* **```np.multiply()```** 一個一個相乘


* **```C=np.transpose (B)```** 一般矩陣乘法:要先轉置 讓基底相同
**```result_1= np.matmul (A , C)```** 矩陣相乘
元素都是0的矩陣 ```np.zeros(shape,dtype =float)```
元素都是1的矩陣 ```np.ones(shape,dtype =float)```
隨機陣列 ==**```np.random.randint(low最小值(含),high(不含),size維度,dtype=int)```**==
```python=
#創造隨機矩陣,隨機給r、g、b的值,單獨秀出r、g灰階影像與三種通道的影像
import numpy as np
import cv2
import sys
import random
r=np.random.randint(20,255,size=(300,300),dtype=np.uint8)
g=np.random.randint(20,255,size=(300,300),dtype=np.uint8)
b=np.random.randint(20,255,size=(300,300),dtype=np.uint8) //uint8 沒有符號int 8bit
img2=cv2.merge((b,g,r)) #三種通道都有(彩色)
img1=cv2.add(r,g) #兩矩陣相加(還是一個通道)(灰階)
cv2.imshow('rgb',img2)
cv2.imshow('gray',img1)
cv2.imshow('r',r)
cv2.imshow('g',g)
k=cv2.waitKey(0)
if k==ord("a"): #按A結束
cv2.destroyAllWindows( )
```
e.g. 20x40的黑色區域

```python=
import numpy as np
import cv2
global img
filename = input( "Please enter filename: " ) #輸入圖片檔名
img = cv2.imread( filename, -1 )
cv2.namedWindow( filename )
#cv2.setMouseCallback( filename, onMouse ) 用滑鼠
cv2.imshow( filename, img )
x=input( "Please enter X: " )
y=input( "Please enter Y: " )
for i in range(20): #用雙層迴圈 i:row的數量 南北向
for j in range(40): #j:colum數 東西向
img[int(x)+i,int(y)+j]=[0,0,0]
cv2.imshow( filename, img )
k=cv2.waitKey(0)
if k==ord("a"):
cv2.destroyAllWindows( )
```
**HW23.用內建的相機捕捉20秒的影片並儲存**.
> cv2.getTickCount ()函數向我們返回從參考事件發送到調用cv2.getTickCount()函數的時間的時鐘信號計數。
>
> 要計算代碼塊執行的時間,我們可以使用cv2.getTickCount()在代碼塊的開頭和結尾計算時鐘信號的數量,並將其差值除以頻率,可以使用cv2.getTickFrequency ()函數。
==**```cv2.getTickCount()```**== 計算兩次getTickCount()之間的時鐘數量/頻率 **```cv2.getTickFrequency ()```** =時間
```python=
import cv2
cap=cv2.VideoCapture(0)
#width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # 取得影像寬度
#height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 取得影像高度
fourcc= cv2.VideoWriter_fourcc(*'mp4v') # 設定儲存的格式
out=cv2.VideoWriter('01.mp4',fourcc,30.0,(640,480))#產生空的影像檔
capture_time = 20.0 #設定影片時長
start_time = cv2.getTickCount() #開始計時
while True:
ret,frame=cap.read()
if not ret:
print("can't recive frame")
break
cv2.imshow('frame',frame)
out.write(frame) #把frame放入影像檔內
elapsed_time = (cv2.getTickCount() - start_time) / cv2.getTickFrequency()
#結束時間=(結束-開始)/頻率
if elapsed_time >= capture_time or cv2.waitKey(1) == ord('q'):
break
cap.release()
out.release()
cv2.destroyAllWindows()
#還是無法固定影片儲存的時長,可能被執行時間/電腦效能影響(會越跑越短)
```
e.g.呈上,再秀出b、g、r三種通道的影像
==**```np.zeros_like(matrix)```**== 創造與matrix相同形狀的矩陣,但元素皆是0
**```cap.get(cv2.CAP_PROP_FRAME_WIDTH)```** 捕捉相機長寬
```python=
#用內建相機分別捕捉(全彩/R/G/B)四種通道並儲存20秒的影片
import cv2
import numpy as np
cap=cv2.VideoCapture(0)
fourcc= cv2.VideoWriter_fourcc(*'mp4v') # 設定儲存的格式
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))#用cap.get()捕捉攝影機的規格
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
out_color=cv2.VideoWriter('01_merge.mp4',fourcc,30.0,(width,height))#產生空的影像檔
out_g=cv2.VideoWriter('01_g.mp4',fourcc,30.0,(width,height))
out_b=cv2.VideoWriter('01_b.mp4',fourcc,30.0,(width,height))
out_r=cv2.VideoWriter('01_r.mp4',fourcc,30.0,(width,height))
capture_time = 20.0 #設定影片時長
start_time = cv2.getTickCount() #開始計時
while True:
ret,frame=cap.read()
if not ret:
print("can't recive frame")
break
b,g,r=cv2.split(frame)#切割成三種通道
matrix=np.zeros_like(b)#創造一個跟b大小相同、元素都是0的矩陣
r_color=cv2.merge((matrix,matrix,r))#合併
g_color=cv2.merge((matrix,g,matrix))
b_color=cv2.merge((b,matrix,matrix))
out_color.write(frame) #把frame放入影像檔內
out_g.write(g_color)
out_r.write(r_color)
out_b.write(b_color)
cv2.imshow('frame',frame)#秀出來
cv2.imshow('b_color',b_color)
cv2.imshow('g_color',g_color)
cv2.imshow('r_color',r_color)
key=cv2.waitKey(1)
elapsed_time = (cv2.getTickCount() - start_time) / cv2.getTickFrequency()
#結束時間=(結束-開始)/頻率
if elapsed_time >= capture_time or key== ord('q'):
break
cap.release()#釋放資源
out_color.release()
out_g.release()
out_b.release()
out_r.release()
cv2.destroyAllWindows()
```
---
## week5_OpenCV基礎影像操作
**ROI:region-of-interest**
```img[200:300,400:500]``` >>> X:200~300 Y:400~500 之間的範圍
### image blending影像融合
**```cv2.addWeighted(img1,a,img2,i-a,gamma)```** 影像融合(兩張圖片大小要相同)
``` python=
#影像融合 用a透明度讓影像漸變到另一張
import numpy as np
import cv2
img1=cv2.imread("test1.png")#圖片大小要一樣
img2=cv2.imread("test2.png")
i=0.00
while True:
i=i+0.01
dst=cv2.addWeighted(img1,i,img2,1-i,50)
cv2.imshow("dst",dst)
print("a=",i)
k=cv2.waitKey(10)#等待時間1000:1秒
if k==ord('s') or i>=1:
cv2.destroyAllWindows()#最後關掉所有視窗
break
```
### 在影像上畫圖
* **```cv2.line()```**
- ( img,(x1,y1),(x2.y2),(B,G,R),Thickness粗細 )
* **```cv2.circle()```**
- -1:填滿
* **```cv2.rectangle()```** 矩形
* **```cv2.ellipse()```** 橢圓
* **```cv2.putText()```**
```python=
#HW33圖中心畫綠色圓+對角紅色線
import cv2
import numpy as np
img1=cv2.imread("test1.png")
x,y,z=img1.shape#影像大小 垂直高度/水平寬度/通道數量
print(x,y)
cv2.line(img1 ,(0,0),(y-1,x-1),(0,0,255),5)
cv2.line(img1 ,(0,x),(y,0),(0,0,255),5)
cv2.circle(img1,(int(y/2),int(x/2)),100,(0,255,0),-1) #(int(y/2):避免不能整除產生小數點
cv2.imshow("draw",img1)
k=cv2.waitKey(0)
cv2.destroyAllWindows()
```
### Color Space色彩空間
* RGB:亮度彩度混在一起
- opencv使用BGR(順序不同喔)
* **HSV**:
- 彩度Hue: in opencv的範圍[0,179]
- 飽和度Saturation:how far is the color from gray [0,255]
- 亮度Value: [0,255]
- 
* YCrCb
- Y:亮度分量
- Cr-Cb:彩度
* 色彩空間
* 分割:
- ```h,s,v=cv2.splite(img)```
- ```y,cr,cb=cv2.split(img)```
* 轉換:
- **```cv2.cvtColor(bgr,cv2.COLOR_BGR2HSV)```**
- **```cv2.cvtColor(bgr,cv2.COLOR_BGR2YCR_CB)```**
#### 正規化
- 制定一個標準或算法,使其有可信的比較基準
- r=R/(R+G+B)、G/(R+G+B)、B/(R+G+B)
- b=1-g-r,r+g+b=1
### 滑桿
**```cv2.createTrackbar('滑桿名稱','windows_name',min,max,Fn)```**
在視窗上產生滑桿 滑動時執行Fn程式
**```cv2.getTrackbarPos('滑桿名稱','視窗名稱')```** 取得滑桿的位置
## week6_Color Object Dection and Tracking
### 創造遮罩
==**```cv2.bitwise_and(frame,frame,msak=mask)```**==
* 做bit_and運算
* mask=mask:要提取的範圍
* frame :執行運算的原始影像
```python=
#將輸入影像和遮罩做bit_and二進位AND運算。只保留在遮罩範圍內的畫素值
import cv2 as cv2
def image_and(image,mask): #輸入影像和遮罩
area = cv2.bitwise_and(image,image,mask=mask) #不打mask的話就是算兩張圖片的交集
cv2.imshow("area",area)
return area
```
==**```cv2.inRange(img,lowerb,upperb)```**== **抓取特定範圍的顏色**
* img輸入的影像
* 色彩範圍最低數值
* 色彩範圍最高數值

```python=!
#color object dection
#捕捉影片中的藍色部分
import cv2
import numpy as np
cap = cv2.VideoCapture('blue_object_video.mp4')
while True:
ret, frame = cap.read()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)#轉成hsv
lower_blue = np.array([110, 50, 50])#設定遮罩範圍(已知HSV中藍色的範圍
high_blue = np.array([130, 255, 255])
mask = cv2.inRange(hsv, lower_blue, high_blue)
res = cv2.bitwise_and(frame, frame, mask=mask)
cv2.imshow('frame',frame)
cv2.imshow('mask',mask)
cv2.imshow('res',res)
if cv2.waitKey(100) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
```
### skin color dection
* range:
* BGR:[0,20,70]~[230,255,255]
* HSV:[0,20,70]~[20,255,255]
* TCrCb: [0,135,85]~[255,180,135]
```pthon=
#skincolor_HSV vs YCrCb
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
# 定義設定HSV和YCrCb顏色範圍的變數
lower_hsv = (0, 20, 70)
upper_hsv = (20, 255, 255)
lower_YCbCr = (0, 138, 85)
upper_YCbCr = (255, 173, 133)
# 不斷從攝影機讀取影像
while True:
# 讀取影像
ret, frame = cap.read()
# 如果影像讀取失敗則退出迴圈
if not ret:
break
# 轉換影像色彩空間為YCrCb、HSV
frame_YCrCb = cv2.cvtColor(frame, cv2.COLOR_BGR2YCR_CB)
frame_hsv=cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
# 膚色偵測(使用HSV)
mask_hsv = cv2.inRange(frame_hsv, lower_hsv, upper_hsv)
res_hsv = cv2.bitwise_and(frame, frame, mask=mask_hsv)
# 膚色偵測(使用YCrCb)
mask_YCbCr = cv2.inRange(frame_YCrCb, lower_YCbCr, upper_YCbCr)
res_YCbCr = cv2.bitwise_and(frame, frame, mask=mask_YCbCr)
cv2.imshow('HSV Skin Detection', res_hsv)
cv2.imshow('YCbCr Skin Detection', res_YCbCr)
if cv2.waitKey(1) == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
```
## week7_Geometric Image Transformation幾何變化

### Scaling、Translation、Rotation
==齊次座標== :X、Y方向之外 **再加一個分量** ,讓所有變換都可以透過矩陣乘法來表示
用 **變換矩陣** transformation matrix 來達成(矩陣乘法)
* 縮放
* 平移
沿(x,y)方向移動:移動量(tx,ty)
1. 用numpy建構M矩陣(變換矩陣translate matrix)
* **```np.float32([ [1,0,100],[0,1,50] ])```**
* 
2. 傳給 ==**```cv2.warpAffine(img,M,(clos,rows) )```**== 執行變換
* (cols,rows)輸出影像的大小(width水平寬,height垂直高)
*
```python=
import cv2
import numpy as np
img=cv2.imread("img_name.jpg",0)
rows,cols=img.shape #用img.shape取得影像大小(垂直高/水平寬/通道)
M=np.float32([ [1,0,100],[0,1,50] ]) #創造變換矩陣M,移動量:(100,50)
dst=cv2.warpAffine(img,M,(cols,rows))
cv2.imshow('img',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
```
* 旋轉Rotation
1. 旋轉角度Θ,使用旋轉矩陣
* 
2. **```cv2.getRotationMatrix2D( (x,y),Θ,比例)```**
* 旋轉中心(x,y)
* 旋轉角度:逆時針為正、順時針為負
* 比例
```python=
import cv2
import numpy as np
img=cv2.imread("img_neme.jpg",0)
rows,cols=img.shape[0:2] #用img.shape取得影像大小(垂直高/水平寬/通道)
M=cv2.getRotationMatrix2D( (cols/2,rows/2),90,1) #創造旋轉矩陣,
dst=cv2.warpAffine(img,M,(cols,rows))
cv2.imshow('img',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
```
### 剪切Shear
將圖像在在水平垂直方向剪切,實現扭曲和變形
### 直方圖
np.reval()降維:多維降成1維
## w11
捲積

```pytho=
#edge dection
import numpy as np
import cv2
cap=cv2.VideoCapture(0)
while True:
ret,frame=cap.read()
frame=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
lo=cv2.Laplacian(frame,cv2.CV_64F)
sobelx=cv2.Sobel(frame,cv2.CV_64F,1,0,ksize=5)
sobely=cv2.Sobel(frame,cv2.CV_64F,0,1,ksize=5)
cv2.imshow('frame',frame)
cv2.imshow("lo",lo)
cv2.imshow("sx",sobelx)
cv2.imshow("sy",sobely)
if cv2.waitKey(1)==ord('s'):
break
cv2.destroyAllWindows
cap.release()
```
```python=
#canny邊緣偵測
#用滑桿控制上下範圍
import cv2
cap = cv2.VideoCapture(0)
# 設定攝影機為640x480
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
def Fn():
print("good")
x=50
y=200
cv2.namedWindow('frame')
cv2.createTrackbar("x","frame",40,80,Fn)
cv2.createTrackbar("y","frame",100,200,Fn)
while True:
ret, frame = cap.read()
if not ret:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 使用高斯模糊降噪
blur = cv2.GaussianBlur(gray, (5, 5), 0)
# 邊緣檢測
edges = cv2.Canny(blur, x, y)
x=cv2.getTrackbarPos('x',"frame")
y=cv2.getTrackbarPos('y',"frame")
cv2.imshow('frame', edges)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
```

## W12
前處理:二值化
## W15形態學
### 1. dilation 膨脹(平移+聯集)

A是原始影像 B是結構化元素(可以自行定義)


### 2. erosion 侵蝕(A-B)

> **```cv2.erode(ing,kernel,iterations=1)```**
### opening 開運算/整形(侵蝕+膨脹)

可以用來 **去除雜訊**
> **```cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel)```**
例:
### closing 閉運算(膨脹+侵蝕)
可以用來 **捕小洞**

> ***```cv2.morfhologyEx(img,v2.MORPH_CLOSE,kernel)```***
### 自定義結構化元素
* 矩形
**```cv2.getStructuringElement(cv2.MORPH_RECT(5,5))```**

* 橢圓
* 十字形
# convolution捲積

功能:
* 補洞、去雜訊 平滑化smoothing
*