最近看 opencv 課程 當個免費仔就筆記下練習內容
helloworld 先在自己的 base 安裝 nb_conda_kernels
他會掃描全部的 conda 環境然後讓 jupyter kernel 加進去, 搞 python 裝環境真的超煩.. 整個心態炸裂
1 2 3 4 5 6 7 8 conda activate base conda install nb_conda_kernels # 切到 cv2 這個環境安裝 ipykernel conda activate cv2 conda install ipykernel conda install opencv conda install matplotlib
接著用 jupyter 開啟, 看是喜歡 notebook or lab 都可以
1 2 jupyter notebook jupyter-lab
helloworld 這裡的 %matplotlib inline
可以直接顯示圖片在 cell 上面 opencv 預設的顏色通道是 bgr
而 matplotlib 則是 rgb
所以需要呼叫 cv2.cvtColor(marmot, cv2.COLOR_BGR2RGB)
先轉為 rgb
1 2 3 4 5 6 7 import cv2 import matplotlib.pyplot as plt %matplotlib inline marmot = cv2.imread('marmot.jpg') marmot = cv2.cvtColor(marmot, cv2.COLOR_BGR2RGB) plt.imshow(marmot) plt.show()
如果想讀成灰度的話需要補上 cv2.IMREAD_GRAYSCALE
接著 matplotlib 要加入 colormap 參數 cmap
可以到這邊來找喜歡的 colormap 以前最常用 jet
這樣就可以得到一隻彩色土撥鼠 XD
1 2 3 4 5 6 marmot = cv2.imread('marmot.jpg', cv2.IMREAD_GRAYSCALE) plt.imshow(marmot, cmap='gray') plt.show() plt.imshow(marmot, cmap='jet') plt.show()
另外讀圖還有這種方法, 不過要記得是按下任意的按鍵, 點關閉視窗是沒用低, jupyter 的格子會掛點
1 2 3 4 5 6 marmot = cv2.imread('marmot.jpg') blur_marmot = cv2.blur(marmot, (3,3)) blur_marmot_rgb = cv2.cvtColor(blur_marmot, cv2.COLOR_BGR2RGB) cv2.imshow('marmot' , marmot) cv2.waitKey(0) cv2.destroyAllWindows()
ROI (Region of Interest) ROI (Region of Interest)
1 2 3 4 5 6 # 先 Y 後 X y_start, y_end = 50, 250 x_start, x_end = 150, 500 roi_marmot = marmot[y_start:y_end,x_start:x_end] plt.imshow(roi_marmot, cmap='jet') plt.show()
可以用 split 分出三個通道的 array 然後調整數值就可以弄出一隻憂鬱的老鼠 LOL
1 2 3 4 5 6 7 8 marmot = cv2.imread('marmot.jpg') b,g,r = cv2.split(marmot) b[:] = 200 deep_blue = cv2.merge((b,g,r)) deep_blue = cv2.cvtColor(deep_blue, cv2.COLOR_BGR2RGB) plt.imshow(deep_blue) plt.show()
外框填充 跑 convolution
的時候邊框會損失一圈, 可以用 copyMakeBorder
來補
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 img = cv2.imread('marmot.jpg') img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) top_size, bottom_size, left_size , right_size = (50,50,50,50) replicate = cv2.copyMakeBorder(img, top_size, bottom_size, left_size , right_size , borderType=cv2.BORDER_REPLICATE) reflect = cv2.copyMakeBorder(img, top_size, bottom_size, left_size , right_size , borderType=cv2.BORDER_REFLECT) reflect101 = cv2.copyMakeBorder(img, top_size, bottom_size, left_size , right_size , borderType=cv2.BORDER_REFLECT101) wrap = cv2.copyMakeBorder(img, top_size, bottom_size, left_size , right_size , borderType=cv2.BORDER_WRAP) constant = cv2.copyMakeBorder(img, top_size, bottom_size, left_size , right_size , borderType=cv2.BORDER_CONSTANT, value=0) plt.subplot(231), plt.imshow(img,'gray'), plt.title('img') plt.subplot(232), plt.imshow(replicate,'gray'), plt.title('replicate') plt.subplot(233), plt.imshow(reflect,'gray'), plt.title('reflect') plt.subplot(234), plt.imshow(reflect101,'gray'), plt.title('reflect101') plt.subplot(235), plt.imshow(wrap,'gray'), plt.title('wrap') plt.subplot(236), plt.imshow(constant,'gray'), plt.title('constant') plt.show()
影像相加(融合) 融合需要調整把兩張圖大小先 resize 成一樣才可以
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 marmot = cv2.imread('marmot.jpg') marmot.shape # (360, 640, 3) sweet_dumpling = cv2.imread('sweet_dumpling.jpg') sweet_dumpling.shape # (1398, 2484, 3) sweet_dumpling_resize = cv2.resize(sweet_dumpling, (640, 360)) # plt.imshow(sweet_dumpling_resize) # plt.show() fusion = cv2.addWeighted(sweet_dumpling_resize, 0.8 , marmot, 0.5 , 0) fusion = cv2.cvtColor(fusion, cv2.COLOR_BGR2RGB) plt.imshow(fusion) plt.show()
threshold 與平滑處理 threshold 門檻值(閾) 閾 (ㄩˋ) 但一堆人會念成 閥 (ㄈㄚˊ) LOL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 marmot_gray = cv2.imread('marmot.jpg', cv2.IMREAD_GRAYSCALE) ret, t1 = cv2.threshold(marmot_gray, 127, 255, cv2.THRESH_BINARY) ret, t2 = cv2.threshold(marmot_gray, 127, 255, cv2.THRESH_BINARY_INV) ret, t3 = cv2.threshold(marmot_gray, 127, 255, cv2.THRESH_TRUNC) ret, t4 = cv2.threshold(marmot_gray, 127, 255, cv2.THRESH_TOZERO) ret, t5 = cv2.threshold(marmot_gray, 127, 255, cv2.THRESH_TOZERO_INV) titles = ['marmot' , 'THRESH_BINARY' , 'THRESH_BINARY_INV' , 'THRESH_TRUNC' , 'THRESH_TOZERO' , 'THRESH_TOZERO_INV'] images = [marmot_gray, t1, t2,t3,t4,t5] for i in range(6): plt.subplot(2, 3 , i + 1) plt.imshow(images[i], 'gray') plt.title(titles[i]) plt.xticks([]) plt.yticks([]) plt.show()
平滑 產噪音點
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # 讀取影像 img = cv2.imread('your_image.jpg') # 取得影像的尺寸 height, width, channels = img.shape # 設定噪點密度(0-100) noise_density = 0.05 # 例如 5% 的噪點 # 生成隨機的噪點矩陣 noise = np.random.rand(height, width) < noise_density # 將噪點設為白色(255) img_with_noise = img.copy() img_with_noise[noise] = 255 # 白色噪點 # 顯示結果 cv2.imshow('Image with White Noise', img_with_noise) cv2.waitKey(0) cv2.destroyAllWindows()
blur (3,3) 為卷積大小, 一般都用奇數
1 2 3 4 5 6 marmot = cv2.imread('marmot.jpg') blur_marmot = cv2.blur(marmot, (3,3)) blur_marmot_rgb = cv2.cvtColor(blur_marmot, cv2.COLOR_BGR2RGB) cv2.imshow('marmot' , marmot) cv2.waitKey(0) cv2.destroyAllWindows()
另一種寫法呼叫 boxFilter 函數
1 2 3 4 5 marmot = cv2.imread('marmot.jpg') blur_marmot = cv2.boxFilter(marmot,-1, (3,3), normalize=True) blur_marmot_rgb = cv2.cvtColor(blur_marmot, cv2.COLOR_BGR2RGB) plt.imshow(blur_marmot_rgb) plt.show()
高斯模糊
取鄰近的讓權重更大些, 大概長這樣 [ [0.6,0.8,0.6], [0.8, 1 ,0.8], [0.6,0.8,0.6], ]
1 2 3 4 5 marmot = cv2.imread('marmot_noise.jpg') blur_marmot = cv2.GaussianBlur(marmot,(5,5), 0) blur_marmot_rgb = cv2.cvtColor(blur_marmot, cv2.COLOR_BGR2RGB) plt.imshow(blur_marmot_rgb) plt.show()
中值因為他取中間的數值, 所以可以有效消除噪音點
1 2 3 4 5 marmot_noise = cv2.imread('marmot_noise.jpg') blur_marmot = cv2.medianBlur(marmot_noise, 3) blur_marmot_rgb = cv2.cvtColor(blur_marmot, cv2.COLOR_BGR2RGB) plt.imshow(blur_marmot_rgb) plt.show()
他這裡教一個技巧可以用 numpy 的 hstack
vstack
把圖拼成一張
1 2 3 4 5 6 7 8 9 10 11 marmot_noise = cv2.imread('marmot_noise.jpg') blur_marmot = cv2.GaussianBlur(marmot_noise,(5,5), 0) blur_marmot2 = cv2.medianBlur(marmot_noise, 3) res = np.hstack((marmot_noise, blur_marmot, blur_marmot2)) res = cv2.cvtColor(res, cv2.COLOR_BGR2RGB) plt.imshow(res) plt.show() # cv2.imshow('res', res) # cv2.waitKey(0) # cv2.destroyAllWindows()
形態學 Morphological 腐蝕 Erosion 通常用在已經二值化的圖上, 先拿小畫家寫個黑底白字的土撥鼠, 然後字上面畫毛
1 2 3 4 5 6 7 8 9 marmot_text = cv2.imread('marmot_text.png') kernel = np.ones((3,3), np.uint8) # kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) print(kernel) print(type(kernel)) marmot_text_erode = cv2.erode(marmot_text , kernel, iterations= 1) res = np.hstack((marmot_text,marmot_text_erode)) plt.imshow(res) plt.show()
膨脹 dilate 跟腐蝕相互為逆, 假如腐蝕過後線條變細可以用膨脹來讓它變粗
1 2 3 4 5 6 7 8 9 marmot_text = cv2.imread('marmot_text.png') kernel = np.ones((3,3), np.uint8) print(kernel) print(type(kernel)) marmot_text_erode = cv2.erode(marmot_text , kernel, iterations= 1) marmot_text_dilate = cv2.dilate(marmot_text_erode , kernel, iterations= 1) res = np.hstack((marmot_text,marmot_text_erode,marmot_text_dilate)) plt.imshow(res) plt.show()
開運算 Opening / 閉運算 Closing 開運算 => 先腐蝕再膨脹 閉運算 => 先膨脹再腐蝕
1 2 3 4 5 6 7 marmot_text = cv2.imread('marmot_text.png') kernel = np.ones((3,3), np.uint8) marmot_text_open = cv2.morphologyEx(marmot_text,cv2.MORPH_OPEN, kernel) marmot_text_close = cv2.morphologyEx(marmot_text,cv2.MORPH_CLOSE, kernel) res = np.hstack((marmot_text_open, marmot_text_close)) plt.imshow(res) plt.show()
梯度運算 Gradient Gradient = 膨脹 Dilation(image) - 腐蝕 Erosion(image)
1 2 3 4 marmot_text = cv2.imread('marmot_text.png') marmot_text_open = cv2.morphologyEx(marmot_text,cv2.MORPH_OPEN, kernel) plt.imshow(res) plt.show()
禮帽 Top Hat / 黑帽 Black Hat TopHat = 原圖 - 開運算結果
1 2 3 4 5 marmot_text = cv2.imread('marmot_text.png') kernel = np.ones((3,3), np.uint8) marmot_text_tophat = cv2.morphologyEx(marmot_text,cv2.MORPH_TOPHAT, kernel) plt.imshow(marmot_text_tophat) plt.show()
BlackHat = 閉運算結果 - 原圖
1 2 3 4 5 marmot_text = cv2.imread('marmot_text.png') kernel = np.ones((3,3), np.uint8) marmot_text_blackhat = cv2.morphologyEx(marmot_text,cv2.MORPH_BLACKHAT, kernel) plt.imshow(marmot_text_blackhat) plt.show()
圖像梯度 Sobel 跟高斯有點像, 越近權重越高
kernelx = [ -1 0 +1 -2 0 +2 -1 0 +1 ]
kernely = [ -1 -2 -1 0 0 0 +1 +2 +1 ]
Gx = kernelx * A and Gy = kernely * A
1 2 3 4 5 6 7 8 9 10 11 marmot = cv2.imread('marmot.jpg', cv2.IMREAD_GRAYSCALE) marmotx = cv2.Sobel(marmot, cv2.CV_64F, 1, 0, ksize=3) marmoty = cv2.Sobel(marmot, cv2.CV_64F, 0, 1, ksize=3) marmotx = cv2.convertScaleAbs(marmotx) marmoty = cv2.convertScaleAbs(marmoty) marmotxy = cv2.addWeighted(marmotx, 0.5, marmoty, 0.5, 0) plt.imshow(marmotxy, cmap='gray') plt.show() # cv2.imshow('marmotxy', marmotxy) # cv2.waitKey(0) # cv2.destroyAllWindows()
scharr / lapkacian scharr 細節會比 Sobel 還更多
kernelx = [ -3 0 3 -10 0 10 -3 0 3 ]
kernely = [ -3 -10 3 0 0 0 -3 -10 -3 ]
Gx = kernelx * A Gy = kernely * A
1 2 3 4 5 6 7 8 marmot = cv2.imread('marmot.jpg', cv2.IMREAD_GRAYSCALE) marmotx = cv2.Scharr(marmot, cv2.CV_64F, 1, 0) marmoty = cv2.Scharr(marmot, cv2.CV_64F, 0, 1) marmotx = cv2.convertScaleAbs(marmotx) marmoty = cv2.convertScaleAbs(marmoty) marmotxy = cv2.addWeighted(marmotx, 0.5, marmoty, 0.5, 0) plt.imshow(marmotxy, cmap='gray') plt.show()
lapkacian 不建議單獨使用, 需要搭配其他咚咚
G = [ 0 1 0 1 -4 1 0 1 0 ]
1 2 3 4 5 marmot = cv2.imread('marmot.jpg', cv2.IMREAD_GRAYSCALE) marmot_laplacian = cv2.Laplacian(marmot, cv2.CV_64F) marmot_laplacian = cv2.convertScaleAbs(marmot_laplacian) plt.imshow(marmot_laplacian, cmap='gray') plt.show()
Retinex 視網膜 參考自這個 repo 還有另外一種 https://github.com/dongb5/Retinex
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 import cv2 import matplotlib.pyplot as plt import numpy as np %matplotlib inline def singleScaleRetinex(img,variance): retinex = np.log10(img) - np.log10(cv2.GaussianBlur(img, (0, 0), variance)) return retinex def multiScaleRetinex(img, variance_list): retinex = np.zeros_like(img) for variance in variance_list: retinex += singleScaleRetinex(img, variance) retinex = retinex / len(variance_list) return retinex def MSR(img, variance_list): img = np.float64(img) + 1.0 img_retinex = multiScaleRetinex(img, variance_list) for i in range(img_retinex.shape[2]): unique, count = np.unique(np.int32(img_retinex[:, :, i] * 100), return_counts=True) for u, c in zip(unique, count): if u == 0: zero_count = c break low_val = unique[0] / 100.0 high_val = unique[-1] / 100.0 for u, c in zip(unique, count): if u < 0 and c < zero_count * 0.1: low_val = u / 100.0 if u > 0 and c < zero_count * 0.1: high_val = u / 100.0 break img_retinex[:, :, i] = np.maximum(np.minimum(img_retinex[:, :, i], high_val), low_val) img_retinex[:, :, i] = (img_retinex[:, :, i] - np.min(img_retinex[:, :, i])) / \ (np.max(img_retinex[:, :, i]) - np.min(img_retinex[:, :, i])) \ * 255 img_retinex = np.uint8(img_retinex) return img_retinex def SSR(img, variance): img = np.float64(img) + 1.0 img_retinex = singleScaleRetinex(img, variance) for i in range(img_retinex.shape[2]): unique, count = np.unique(np.int32(img_retinex[:, :, i] * 100), return_counts=True) for u, c in zip(unique, count): if u == 0: zero_count = c break low_val = unique[0] / 100.0 high_val = unique[-1] / 100.0 for u, c in zip(unique, count): if u < 0 and c < zero_count * 0.1: low_val = u / 100.0 if u > 0 and c < zero_count * 0.1: high_val = u / 100.0 break img_retinex[:, :, i] = np.maximum(np.minimum(img_retinex[:, :, i], high_val), low_val) img_retinex[:, :, i] = (img_retinex[:, :, i] - np.min(img_retinex[:, :, i])) / \ (np.max(img_retinex[:, :, i]) - np.min(img_retinex[:, :, i])) \ * 255 img_retinex = np.uint8(img_retinex) return img_retinex
呼叫法
MSR 需要吃一個 array AI 建議用這樣 variance_list = [15, 80, 200] SSR 只需要單一數值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 variance_list = [15, 80, 200] variance = 300 # 原圖 img = cv2.imread('sweet_dumpling.jpg') img_plt = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) plt.imshow(img_plt) plt.show() # MSR img_msr = MSR(img,variance_list) img_msr_plt = cv2.cvtColor(img_msr, cv2.COLOR_BGR2RGB) plt.imshow(img_msr_plt) plt.show() # SSR img_ssr = SSR(img, variance) img_ssr_plt = cv2.cvtColor(img_ssr, cv2.COLOR_BGR2RGB) plt.imshow(img_ssr_plt) plt.show()