## Feature dection SIFT尺度不變特徵轉換 影像中的局部特徵 1. 偵測 2. 描述 # 視訊分析 [TOC] ## week2_introduction Digitization數位化:用3步驟,將float像素值數位化為數位影像 1. Sampling : 將時間軸離散化 2. **==Quantization 量化==** : 將振幅軸切割成有限的位階(level),將 **振幅軸離散化,浮點數變成整數** 3. Coding : 整數值編成二元碼 物體經由鏡片投影產生影像(連續的), ![](https://i.imgur.com/WbICvcD.png) 生成取樣影像(像素位置是離散的、色彩值是連續的),量化將色彩值轉為離散數值 ![](https://i.imgur.com/M1Zw3Re.png) 取樣(產生離散的位置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表示彩色影像 ![](https://i.imgur.com/mlw94cE.png) --- ## 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. ![](https://i.imgur.com/rz9dzKf.png) >超過0~255範圍 會不自然 圖片的型態```img.shape``` :秀出圖像的長垂直、寬水平、色彩(灰階1 黑白2 彩色3) ```python! print("color_img_shap",img.shap) ``` ![](https://i.imgur.com/SWS9ifa.png) **```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 ![](https://i.imgur.com/rlOzXKZ.png) ``` x=np.array([ [1,2,3],[4,5,6] ]) ``` #### 陣列屬性: * np.ndim維度 * np.shap外型 #(row,column) * np.size大小 * np.dtype資料內容:(unit8) #### 陣列+-* /運算: **element-wise:*/是一個元素對一個元素運算** * **```np.multiply()```** 一個一個相乘 ![](https://i.imgur.com/OyOscZz.png) ![](https://i.imgur.com/BdbGvuu.png) * **```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的黑色區域 ![](https://i.imgur.com/qMCJtUj.jpg) ```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] - ![](https://i.imgur.com/D631FFq.png) * 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輸入的影像 * 色彩範圍最低數值 * 色彩範圍最高數值 ![](https://i.imgur.com/AaCX5Tz.png) ```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幾何變化 ![](https://i.imgur.com/7gKC0Gy.png) ### Scaling、Translation、Rotation ==齊次座標== :X、Y方向之外 **再加一個分量** ,讓所有變換都可以透過矩陣乘法來表示 用 **變換矩陣** transformation matrix 來達成(矩陣乘法) * 縮放 * 平移 沿(x,y)方向移動:移動量(tx,ty) 1. 用numpy建構M矩陣(變換矩陣translate matrix) * **```np.float32([ [1,0,100],[0,1,50] ])```** * ![](https://i.imgur.com/ukdWZHY.png) 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. 旋轉角度Θ,使用旋轉矩陣 * ![](https://i.imgur.com/47HAdlh.png) 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 捲積 ![](https://i.imgur.com/uDbOWCZ.png) ```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() ``` ![](https://i.imgur.com/aJq0Q3V.png) ## W12 前處理:二值化 ## W15形態學 ### 1. dilation 膨脹(平移+聯集) ![](https://hackmd.io/_uploads/rkxHLlOnr3.png) A是原始影像 B是結構化元素(可以自行定義) ![](https://hackmd.io/_uploads/SyRIZu2S2.png) ![](https://hackmd.io/_uploads/H1JpWuhHn.png) ### 2. erosion 侵蝕(A-B) ![](https://hackmd.io/_uploads/ByeFzO2B3.png) > **```cv2.erode(ing,kernel,iterations=1)```** ### opening 開運算/整形(侵蝕+膨脹) ![](https://hackmd.io/_uploads/S1F6Qu2Hn.png) 可以用來 **去除雜訊** > **```cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel)```** 例:![](https://hackmd.io/_uploads/Byzc4_2S2.png) ### closing 閉運算(膨脹+侵蝕) 可以用來 **捕小洞** ![](https://hackmd.io/_uploads/H1KNrO3Hh.png) > ***```cv2.morfhologyEx(img,v2.MORPH_CLOSE,kernel)```*** ### 自定義結構化元素 * 矩形 **```cv2.getStructuringElement(cv2.MORPH_RECT(5,5))```** ![](https://hackmd.io/_uploads/B1I9qKnrn.png) * 橢圓 * 十字形 # convolution捲積 ![image](https://hackmd.io/_uploads/ByZfyQWTyx.png) 功能: * 補洞、去雜訊 平滑化smoothing *