
opencv學習筆記
- 顏色空間
- 改變顏色空間(cv2.cvtColor())
- 目標追蹤
- 如何查找某個顏色的HSV值
- 圖形變換
- 縮放(cv2.resize())
- 平移
- 旋轉
- 仿射變換
- 透視變換
- 二值化
- 簡單閾值法
- 自適應閾值
- Otsu二值化(俗稱大津法)
- Otsu 二值化原理
- 濾波
- 二維卷積(影像濾波)
- 影像模糊(影像平滑)
- 均值濾波
- 高斯濾波
- 中值濾波
- 雙邊濾波
顏色空間
改變顏色空間(cv2.cvtColor())
在 OpenCV 中有超過 150 種顏色空間轉換的方法,但我們僅需要研究兩個最常使用的方法,他們是 BGR 到 GRAY,BGR 到 HSV,
我們使用 cv.cvtColor(input_image, flag)函式進行顏色轉換,其中 flag 決定了轉換的型別,
對于 BGR 到 Gray 轉換我們令 flag 為 cv2.COLOR_BGR2GRAY, 同樣,對于 BGR 到 HSV, 我們令 flag 為 cv2.COLOR_BGR2HSV,
代碼:
import cv2
BGR = cv2.imread("C:/Users/Zhang-Lei/Desktop/snack.png")
GRAY = cv2.cvtColor(BGR, cv2.COLOR_BGR2GRAY)
HSV = cv2.cvtColor(BGR, cv2.COLOR_BGR2HSV)
cv2.imshow('BGR', BGR)
cv2.imshow('GRAY', GRAY)
cv2.imshow('HSV', HSV)
cv2.resizeWindow('BGR', 500, 800)
cv2.resizeWindow('GRAY', 500, 800)
cv2.resizeWindow('HSV', 500, 800)
cv2.waitKey()
cv2.destroyAllWindows()
結果:

如想得到其他 flag 值,我們只需要輸入代碼查看:
代碼:
flags = [i for i in dir(cv2) if i.startswith('COLOR_')]
print(flags)
種類過多,這里就不展示結果了,
注意: 對于 HSV, 色調(Hue)范圍為 [0,179], 飽和度(Saturation)范圍為 [0,255] ,明亮度(Value)為 [0,255]. 不同的軟體使用不同的比例. 所以如果你想用 OpenCV 的值與別的軟體的值作對比,你需要歸一化這些范圍,
目標追蹤
現在我們知道了如何將 BGR 圖片轉化為 HSV 圖片,我們可以使用它去提取彩色物件,HSV 比 BGR 在顏色空間上更容易表示顏色,在我們的應用中,我們會嘗試提取一個藍色的彩色物件,方法為:
- 提取每一視頻幀,
- 將 BGR 轉化為 HSV 顏色空間,
- 我們用藍色像素的范圍對該 HSV 圖片做閾值,
- 現在提取出了藍色物件,我們可以隨意處理圖片了 ,
代碼:
import cv2
import numpy as np
cap = cv2.VideoCapture(0) # 讀取攝像頭影像
while 1:
_, frame = cap.read()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # 轉為HSV圖
lower_blue = np.array([110, 50, 50]) # 限定藍色范圍下限
upper_blue = np.array([130, 255, 255]) # 限定藍色范圍上限
mask = cv2.inRange(hsv, lower_blue, upper_blue) # 限定HSV影像范圍
res = cv2.bitwise_and(frame, frame, mask=mask) # 按位與
cv2.imshow('frame', frame)
cv2.imshow('mask', mask)
cv2.imshow('res', res)
k = cv2.waitKey(5) & 0xFF # 檢測是否按下Esc 參考&0xff,
# 正是為了只取按鍵對應的ASCII值后8位來排除不同按鍵的干擾進行判斷按鍵是什么,
if k == 27:
break
cv2.destroyAllWindows()
結果:

可以看到這本書的藍色物件被我們提取了出來,
如何查找某個顏色的HSV值
你可以使用相同的函式:cv2.cvtColor(), 不需要輸入圖片,你只需要輸入你需要的 BGR 值即可. 例如, 為了找到綠色的 HSV 值,
代碼:
green = np.uint8([[[0, 255, 0]]])
hsv_green = cv2.cvtColor(green, cv2.COLOR_BGR2HSV)
print(hsv_green)
結果:

圖形變換
縮放(cv2.resize())
縮放是調整圖片的大小, OpenCV 使用 cv2.resize() 函式進行調整,可以手動指定影像的大小,也可以指定比例因子,可以使用不同的插值方法,對于下采樣(影像上縮小),最合適的插值方法是 cv2.INTER_AREA 對于上采樣(放大),最好的方法是 cv2.INTER_CUBIC (速度較慢)和 cv2.INTER_LINEAR (速度較快),默認情況下,所使用的插值方法都是 cv2.INTER_AREA ,你可以使用如下方法調整輸入圖片大小:
代碼:
import cv2
img = cv2.imread("C:/Users/Zhang-Lei/Desktop/snack.png")
res1 = cv2.resize(img, None, fx=1/2, fy=1/2, interpolation=cv2.INTER_CUBIC) # fx和fy分別對應橫縱軸影像放大倍數
height, width = img.shape[:2]
res2 = cv2.resize(img, (width//2, height//2), interpolation=cv2.INTER_CUBIC) # 第二種方法
cv2.imshow('img', img)
cv2.imshow('res1', res1)
cv2.imshow('res2', res2)
cv2.waitKey()
cv2.destroyAllWindows()
結果:

可以看出兩種方法都將圖片的大小縮小為原來大小的一半,
平移
平移變換是物體位置的移動,如果知道 (x,y) 方向的偏移量,假設為 (t_x,t_y),則可以創建如下轉換矩陣 M:

您可以將變換矩陣存為 np.float32 型別的 numpy 陣列,并將其作為 cv.warpAffine 的第二個引數,請參見以下轉換(100,50)的示例:
代碼:
import cv2
import numpy as np
img = cv2.imread('C:/Users/Zhang-Lei/Desktop/snack_gray.png', cv2.IMREAD_UNCHANGED)
rows, cols = img.shape
M = np.float32([[1, 0, 100], [0, 1, 50]])
res = cv2.warpAffine(img, M, (cols, rows))
cv2.imshow('img', img)
cv2.imshow('res', res)
cv2.waitKey()
cv2.destroyAllWindows()
結果:

這里可以看到得到的影像相比于原影像是向右下方平移了的,
注意:cv2.warpAffine 函式的第三個引數是輸出影像的大小,其形式應為(寬度、高度),記住寬度=列數,高度=行數,
旋轉
以θ角度旋轉圖片的轉換矩陣形式為:

但 Opencv 提供了可變旋轉中心的比例變換,所以你可以在任意位置旋轉圖片,修改后的轉換矩陣為:

其中:

為了找到這個轉換矩陣,opencv 提供了一個函式
cv2.getRotationMatrix2D ,
請查看下面的示例,它將影像相對于中心旋轉 90 度,而不進行任何縮放,
代碼:
import cv2
img = cv2.imread('C:/Users/Zhang-Lei/Desktop/snack_gray.png', cv2.IMREAD_UNCHANGED)
rows, cols = img.shape
M = cv2.getRotationMatrix2D(((cols-1)/2.0, (rows-1)/2.0), 90, 1)
res = cv2.warpAffine(img, M, (cols, rows))
cv2.imshow('img', img)
cv2.imshow('res', res)
cv2.waitKey()
cv2.destroyAllWindows()
結果:

可見圖片已經發生了逆時針90°的旋轉,
仿射變換
在仿射變換中,原始影像中的所有平行線在輸出影像中仍然是平行的,為了找到變換矩陣,我們需要從輸入影像中取三個點及其在輸出影像中的對應位置,然后 cv2.getPerspectiveTransform 將創建一個 2x3 矩陣,該矩陣將傳遞給cv2.warpAffine ,
代碼:
import cv2
import matplotlib.pyplot as plt
import numpy as np
img = cv2.imread('C:/Users/Zhang-Lei/Desktop/drawing.png')
rows, cols, ch = img.shape
pts1 = np.float32([[50, 50], [200, 50], [50, 200]])
pts2 = np.float32([[10, 100], [200, 50], [100, 250]])
M = cv2.getAffineTransform(pts1, pts2)
dst = cv2.warpAffine(img, M, (cols, rows))
plt.subplot(121), plt.imshow(img), plt.title('Input')
plt.subplot(122), plt.imshow(dst), plt.title('Output')
plt.show()
結果:

透視變換
對透視轉換,你需要一個 3x3 變換矩陣,即使在轉換之后,直線也將保持直線,要找到這個變換矩陣,需要輸入影像上的 4 個點和輸出影像上的相應點,在這四點中,任意三點不應該共線,然后通過 cv2.getPerspectiveTransform 找到變換矩陣,然后對這個 3x3 變換矩陣使用 cv2.warpPerspective,
代碼:
import cv2
import matplotlib.pyplot as plt
import numpy as np
img = cv2.imread('C:/Users/Zhang-Lei/Desktop/sudoku.png')
rows, cols, ch = img.shape
pts1 = np.float32([[56, 65], [368, 52], [28, 387], [389, 390]])
pts2 = np.float32([[0, 0], [300, 0], [0, 300], [300, 300]])
M = cv2.getPerspectiveTransform(pts1, pts2)
dst = cv2.warpPerspective(img, M, (300, 300))
plt.subplot(121), plt.imshow(img), plt.title('Input')
plt.subplot(122), plt.imshow(dst), plt.title('Output')
plt.show()
結果:

二值化
簡單閾值法
此方法是直截了當的,如果像素值大于閾值,則會被賦為一個值(可能為白色),否則會賦為另一個值(可能為黑色),使用的函式是 cv2.threshold,第一個引數是源影像,它應該是灰度影像,第二個引數是閾值,用于對像素值進行分類,第三個引數是 maxval,它表示像素值大于(有時小于)閾值時要給定的值,opencv 提供了不同型別的閾值,由函式的第四個引數決定,不同的型別有:
- cv.THRESH_BINARY
- cv.THRESH_BINARY_INV
- cv.THRESH_TRUNC
- cv.THRESH_TOZERO
- cv.THRESH_TOZERO_INV
獲得兩個輸出,第一個是 retval,稍后將解釋,第二個輸出是我們的閾值影像,
代碼:
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('C:/Users/Zhang-Lei/Desktop/snack_gray.png', cv2.IMREAD_UNCHANGED)
ret1, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
ret2, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
ret3, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
ret4, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
ret5, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
titles = ['Original', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
plt.subplot(2, 3, i+1)
plt.imshow(images[i], cmap='gray')
plt.title(titles[i])
plt.xticks([])
plt.yticks([])
plt.show()
結果:

可以看到,其他五幅影像都以不同的方式區分像素,
自適應閾值
在前一節中,我們使用一個全域變數作為閾值,但在影像在不同區域具有不同照明條件的條件下,這可能不是很好,在這種情況下,我們采用自適應閾值,在此,演算法計算影像的一個小區域的閾值,因此,我們得到了同一影像不同區域的不同閾值,對于不同光照下的影像,得到了更好的結果,
它有三個“特殊”輸入引數,只有一個輸出引數,
-
Adaptive Method :它決定如何計算閾值,
-
cv2.ADAPTIVE_THRESH_MEAN_C :閾值是指鄰近地區的平均值,
-
cv2.ADAPTIVE_THRESH_GAUSSIAN_C :閾值是權重為高斯窗的鄰域值的加權和,
-
Block Size :它決定了計算閾值的視窗區域的大小,
-
C :它只是一個常數,會從平均值或加權平均值中減去該值,
下面的代碼比較了具有不同照明的影像的全域閾值和自適應閾值:
代碼:
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('C:/Users/Zhang-Lei/Desktop/snack_gray.png', 0)
img = cv2.medianBlur(img, 5)
ret, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
thresh2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY, 11, 2)
thresh3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY, 11, 2)
titles = ['Original Image', 'Global Thresholding (v = 127)',
'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, thresh1, thresh2, thresh3]
for i in range(4):
plt.subplot(2, 2, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
結果:

Otsu二值化(俗稱大津法)
在第一部分中,有一個引數 retval,當我們進行 Otsu 二值化時,它的用途就來了,那是什么?
在全域閾值化中,我們使用一個任意的閾值,對嗎?那么,我們如何知道我們選擇的值是好的還是不好的呢?答案是,試錯法,但是考慮一個雙峰影像(簡單來說,雙峰影像是一個直方圖有兩個峰值的影像),對于那個影像,我們可以近似地取這些峰值中間的一個值作為閾值,對嗎?這就是 Otsu 二值化所做的,所以簡單來說,它會自動從雙峰影像的影像直方圖中計算出閾值,(對于非雙峰影像,二值化將不準確,)
為此,我們使用了 cv2.threshold 函式,但傳遞了一個額外的符號 cv2.THRESH_OTSU ,對于閾值,只需傳入零,然后,該演算法找到最佳閾值,并作為第二個輸出回傳 retval,如果不使用 otsu 閾值,則 retval 與你使用的閾值相同,
查看下面的示例,輸入影像是噪聲影像,在第一種情況下,我應用了值為 127 的全域閾值,在第二種情況下,我直接應用 otsu 閾值,在第三種情況下,我使用 5x5 高斯核過濾影像以去除噪聲,然后應用 otsu 閾值,查看噪聲過濾如何改進結果,
代碼:
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('C:/Users/Zhang-Lei/Desktop/noisy.png', 0)
# 全域閾值
ret1, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# Otsu 閾值
ret2, thresh2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 經過高斯濾波的 Otsu 閾值
blur = cv2.GaussianBlur(img, (5, 5), 0)
ret3, thresh3 = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 畫出所有的影像和他們的直方圖
images = [img, 0, thresh1, img, 0, thresh2, blur, 0, thresh3]
titles = ['Original Noisy Image', 'Histogram', 'Global Thresholding (v=127)',
'Original Noisy Image', 'Histogram', "Otsu's Thresholding",
'Gaussian filtered Image', 'Histogram', "Otsu's Thresholding"]
for i in range(3):
plt.subplot(3, 3, i * 3 + 1), plt.imshow(images[i * 3], 'gray')
plt.title(titles[i * 3]), plt.xticks([]), plt.yticks([])
plt.subplot(3, 3, i * 3 + 2), plt.hist(images[i * 3].ravel(), 256)
plt.title(titles[i * 3 + 1]), plt.xticks([]), plt.yticks([])
plt.subplot(3, 3, i * 3 + 3), plt.imshow(images[i * 3 + 2], 'gray')
plt.title(titles[i * 3 + 2]), plt.xticks([]), plt.yticks([])
plt.show()
結果:

Otsu 二值化原理
本節演示了 otsu 二值化的 python 實作,以展示它的實際作業原理,
由于我們使用的是雙峰影像,因此 Otsu 的演算法試圖找到一個閾值(t),該閾值將由下式計算得到的類內加權方差最小化:

其中:

它實際上找到一個 T 值,它位于兩個峰值之間,使得兩個類的方差最小,它可以簡單地在 python 中實作,如下所示:
代碼:
import cv2
import numpy as np
img = cv2.imread('C:/Users/Zhang-Lei/Desktop/noisy.png', 0)
blur = cv2.GaussianBlur(img, (5, 5), 0)
# 找到歸一化直方圖還有累計分布函式
hist = cv2.calcHist([blur], [0], None, [256], [0, 256])
hist_norm = hist.ravel() / hist.max()
Q = hist_norm.cumsum()
bins = np.arange(256)
fn_min = np.inf
thresh = -1
for i in range(1, 256):
p1, p2 = np.hsplit(hist_norm, [i]) # 概率
q1, q2 = Q[i], Q[255] - Q[i] # 類別總和
b1, b2 = np.hsplit(bins, [i]) # 權重
# f 找到均值與方差
m1, m2 = np.sum(p1 * b1) / q1, np.sum(p2 * b2) / q2
v1, v2 = np.sum(((b1 - m1) ** 2) * p1) / q1, np.sum(((b2 - m2) ** 2) * p2) / q2
# 計算最小函式
fn = v1 * q1 + v2 * q2
if fn < fn_min:
fn_min = fn
thresh = i
# 用 Opencv2 函式的 otsu'閾值
ret, otsu = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
print("{} {}".format(thresh, ret))
結果:

濾波
二維卷積(影像濾波)
與一維信號一樣,影像也可以通過各種低通濾波器(LPF)、高通濾波器(HPF)等進行過濾,LPF 有助于消除噪音、模糊影像等,HPF 濾波器有助于在影像中找到邊緣,
opencv 提供了函式 cv2.filter2D(),用于將內核與影像卷積起來,作為一個例子,我們將嘗試對影像進行均值濾波操作,5x5 均值濾波卷積核如下:

操作如下:將該內核中心與一個像素對齊,然后將該內核下面的所有 25 個像素相加,取其平均值,并用新的平均值替換這個25x25視窗的中心像素,它繼續對影像中的所有像素執行此操作,試試下面這段代碼并觀察結果:
代碼:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('C:/Users/Zhang-Lei/Desktop/noisy.png')
kernel = np.ones((5, 5), np.float32) / 25
dst = cv2.filter2D(img, -1, kernel)
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(dst), plt.title('Averaging')
plt.xticks([]), plt.yticks([])
plt.show()
結果:

可見影像在濾波的作用下變得模糊了,
影像模糊(影像平滑)
影像模糊是通過將影像與低通濾波核卷積來實作的,它有助于消除噪音,它實際上從影像中洗掉高頻內容(例如:噪聲、邊緣),所以在這個操作中邊緣有點模糊,(好吧,有一些模糊技術不會使邊緣太模糊),OpenCV 主要提供四種模糊技術,
均值濾波
這是通過用一個歸一化的濾波器內核與影像卷積來完成的,它只需取內核區域下所有像素的平均值并替換中心元素,這是通過函式 cv2.blur()或 cv2.boxFilter() 完成的,有關內核的更多詳細資訊,請查看檔案,我們應該指定濾波器內核的寬度和高度,3x3 標準化框濾波器如下所示:

注意 :如果你不用標準化濾波,使用cv2.boxFilter(),傳入 normalize=False 引數,
5x5 核的簡單應用如下所示:
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('C:/Users/Zhang-Lei/Desktop/opencv.jpg')
blur = cv2.blur(img, (5, 5))
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(blur), plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.show()
結果:

高斯濾波
在這種情況下,使用高斯核代替了核濾波器,它是通過函式 **cv2.GaussianBlur()**完成的,我們應該指定內核的寬度和高度,它應該是正數并且是奇數(奇數才有一個中位數),我們還應該分別指定 x 和 y 方向的標準偏差、sigmax 和 sigmay,如果只指定 sigmax,則 sigmay 與 sigmax 相同,如果這兩個值都是 0,那么它們是根據內核大小計算出來的,高斯模糊是消除影像高斯噪聲的有效方法,
如果需要,可以使用函式 cv.getGaussianKernel() 創建高斯內核,
上述代碼可以修改為高斯濾波器:
blur = cv2.GaussianBlur(img,(5,5),0)
代碼:
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('C:/Users/Zhang-Lei/Desktop/opencv.jpg')
blur = cv2.GaussianBlur(img, (5, 5), 0)
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(blur), plt.title('GaussianBlurred')
plt.xticks([]), plt.yticks([])
plt.show()
結果:

中值濾波
在這里,函式 cv2.medianBlur() 取內核區域下所有像素的中值,將中央元素替換為該中值,這對影像中的椒鹽噪聲非常有效,有趣的是,在上面的過濾器中,中心元素是一個新計算的值,它可能是影像中的像素值,也可能是一個新值,但在中值模糊中,中心元素總是被影像中的一些像素值所取代,可以有效降低噪音,它的內核大小應該是一個正的奇數整數,
在這個演示中,我在原始影像中添加了 5000個椒鹽噪聲,并應用了中間模糊,只需要將上面代碼改為中值濾波器:
median = cv2.medianBlur(img,5)
代碼:
import random
import cv2
from matplotlib import pyplot as plt
# 添加椒鹽噪聲
def noise(dst):
height, width = dst.shape[:2]
for _ in range(height*width//4):
x = random.randint(0, height-1)
y = random.randint(0, width-1)
dst[x, y] = 255 # 添加鹽噪聲
p = random.randint(0, height-1)
q = random.randint(0, width-1)
dst[p, q] = 0 # 添加椒噪聲
img = cv2.imread('C:/Users/Zhang-Lei/Desktop/snack_gray.png', 0)
plt.subplot(131), plt.imshow(img, cmap='gray'), plt.title('Original')
plt.xticks([]), plt.yticks([])
noise(img)
plt.subplot(132), plt.imshow(img, cmap='gray'), plt.title('Noise')
plt.xticks([]), plt.yticks([])
median = cv2.medianBlur(img, 5)
plt.subplot(133), plt.imshow(median, cmap='gray'), plt.title('MedianBlurred')
plt.xticks([]), plt.yticks([])
plt.show()
結果:

可以看到,第二張照片是在原圖的基礎上添加了噪聲,第三張圖片雖然去除了噪聲,但仍然會有部分模糊,
雙邊濾波
cv2.bilateralFilter() 在保持邊緣銳利的同時,對噪聲去除非常有效,但與其他過濾器相比,操作速度較慢,我們已經看到高斯濾波器取像素周圍的鄰域并找到其高斯加權平均值,該高斯濾波器是一個空間函式,即在濾波時考慮相鄰像素,但是它不考慮像素是否具有幾乎相同的強度,也不考慮像素是否是邊緣像素,所以它也會模糊邊緣,這是我們不想做的,
雙邊濾波器在空間上也采用高斯濾波器,而另一個高斯濾波器則是像素差的函式,空間的高斯函式確保模糊只考慮鄰近像素,而強度差的高斯函式確保模糊只考慮與中心像素強度相似的像素,所以它保留了邊緣,因為邊緣的像素會有很大的強度變化,
下面的示例顯示使用雙邊濾波,同樣的,只需要將上面代碼改為雙邊濾波器:
blur = cv2.bilateralFilter(img,9,75,75)
代碼:
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('C:/Users/Zhang-Lei/Desktop/snack_gray.png', 0)
plt.subplot(121), plt.imshow(img, cmap='gray'), plt.title('Original')
plt.xticks([]), plt.yticks([])
blur = cv2.bilateralFilter(img, 9, 75, 75)
plt.subplot(122), plt.imshow(blur, cmap='gray'), plt.title('BilateralBlurred')
plt.xticks([]), plt.yticks([])
plt.show()
結果:

可見影像經過雙邊濾波變得模糊了一些,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/302711.html
標籤:AI
上一篇:import torchvision錯誤提示ImportError: DLL load failed: 找不到指定的模塊。
