文章目錄
- 濾波演算法
- 準備
- Sobel算子
- 銳化算子
- 高斯濾波算子
- 均值濾波
- 中值濾波
- 最優梯度幅值邊緣檢測演算法
- 影像二值化方法
- 全域迭代法
- 大津法
- 獲取影像中的輪廓,對影像中的目標進行計數
- 參考blog
濾波演算法
cv.filter2D()
這個就是我們用來濾波的函式,作用大概就是根據傳入的圖片和kernel來對圖片進行卷積
引數:
src: 原影像ddepth: 目標影像深度(指資料型別)kernel: 卷積核anchor: 卷積錨點delta: 偏移量,卷積結果要加上這個數字borderType: 邊緣型別
卷積函式(目前只能處理單通道圖片)
(now:這個函式實作的不好,濾波效果很差,不用了)
def imgConvolve(image, kernel):
'''
:param image: 圖片矩陣
:param kernel: 濾波視窗
:return:卷積后的矩陣
'''
img_h = int(image.shape[0])
img_w = int(image.shape[1])
kernel_h = int(kernel.shape[0])
kernel_w = int(kernel.shape[1])
# padding
padding_h = int((kernel_h - 1) / 2)
padding_w = int((kernel_w - 1) / 2)
convolve_h = int(img_h + 2 * padding_h)
convolve_W = int(img_w + 2 * padding_w)
# 分配空間
img_padding = np.zeros((convolve_h, convolve_W))
# 中心填充圖片
img_padding[padding_h:padding_h + img_h, padding_w:padding_w + img_w] = image[:, :]
# 卷積結果
image_convolve = np.zeros(image.shape)
# 卷積
for i in range(padding_h, padding_h + img_h):
for j in range(padding_w, padding_w + img_w):
image_convolve[i - padding_h][j - padding_w] = int(
np.sum(img_padding[i - padding_h:i + padding_h+1, j - padding_w:j + padding_w+1]*kernel))
return image_convolve
準備
匯入影像
img_bubble=cv.imread('bubble.jpg',0)# 單通道讀入
img_rice=cv.imread('rice.png',0)# 單通道讀入
bubble:

rice:

Sobel算子
豎邊濾波
可以過濾出影像中豎直方向的邊
kernel_col = np.array([[-1,0,1],# 提取豎邊的卷積核
[-1,0,1],
[-1,0,1]])
dst = cv.filter2D(img_rice, -1, kernel_col)
plt.figure(figsize=(8,8)) #設定視窗大小
plt.suptitle('col filter') # 圖片名稱
plt.subplot(2,2,1)
plt.imshow(img_rice,cmap='gray')
# cv.imshow('rice',img_rice)
plt.subplot(2,2,2)
plt.imshow(dst,cmap='gray')
# cv.imshow('median',dst)
# cv.waitKey(0)

橫邊濾波
可以過濾出影像中水平方向的邊
kernel_row = np.array([[-1,-1,-1],# 提取橫邊的卷積核
[0,0,0],
[1,1,1]])
dst = cv.filter2D(img_rice, -1, kernel_row)
plt.figure(figsize=(8,8)) #設定視窗大小
plt.suptitle('row filter') # 圖片名稱
plt.subplot(2,2,1)
plt.imshow(img_rice,cmap='gray')
# cv.imshow('rice',img_rice)
plt.subplot(2,2,2)
plt.imshow(dst,cmap='gray')
# cv.imshow('median',dst)
# cv.waitKey(0)

銳化算子
可以將影像進行銳化
kernel_sharp = np.array([[0, -1, 0],
[-1, 5, -1],
[0, -1, 0]])
dst = cv.filter2D(img_rice, -1, kernel_sharp)
plt.figure(figsize=(8,8)) #設定視窗大小
plt.suptitle('gaussian filter') # 圖片名稱
plt.subplot(2,2,1)
plt.imshow(img_rice,cmap='gray')
# cv.imshow('rice',img_rice)
plt.subplot(2,2,2)
plt.imshow(dst,cmap='gray')
# cv.imshow('median',dst)
# cv.waitKey(0)

高斯濾波算子
高斯濾波是一種線性平滑濾波,適用于消除高斯噪聲,使得影像變得平滑
每一個像素點的值,都由其本身和鄰域內的其他像素值經過加權平均后得到
dst = cv.GaussianBlur(img_rice, (11, 11), -1)
plt.figure(figsize=(8,8)) #設定視窗大小
plt.suptitle('gaussian filter') # 圖片名稱
plt.subplot(2,2,1)
plt.imshow(img_rice,cmap='gray')
# cv.imshow('rice',img_rice)
plt.subplot(2,2,2)
plt.imshow(dst,cmap='gray')
# cv.imshow('median',dst)
# cv.waitKey(0)

均值濾波
均值濾波是典型的線性濾波演算法,每一像素點的灰度值,為該點某鄰域視窗內的所有像素點灰度值的平均值
dst = cv.blur(img_rice, (11, 11))
plt.figure(figsize=(8,8)) #設定視窗大小
plt.suptitle('Mean filter') # 圖片名稱
plt.subplot(2,2,1)
plt.imshow(img_rice,cmap='gray')
# cv.imshow('rice',img_rice)
plt.subplot(2,2,2)
plt.imshow(dst,cmap='gray')
# cv.imshow('median',dst)
# cv.waitKey(0)

中值濾波
中值濾波法是一種非線性平滑技術,每一像素點的灰度值,為該點某鄰域視窗內的所有像素點灰度值的中值
dst = cv.medianBlur(img_rice, 11)
plt.figure(figsize=(8,8)) #設定視窗大小
plt.suptitle('Median filter') # 圖片名稱
plt.subplot(2,2,1)
plt.imshow(img_rice,cmap='gray')
# cv.imshow('rice',img_rice)
plt.subplot(2,2,2)
plt.imshow(dst,cmap='gray')
# cv.imshow('median',dst)
# cv.waitKey(0)

最優梯度幅值邊緣檢測演算法
對米粒影像進行四個方向的濾波,提取出米粒的邊緣
這個演算法我實作的并不好,運行的可能會比較慢
# cv.imshow('rice',img_rice)
plt.figure(figsize=(8,8)) #設定視窗大小
plt.suptitle('Optimal gradient magnitude') # 圖片名稱
plt.subplot(2,2,1)
plt.imshow(img_rice,cmap='gray')
kernel_col = np.array([[-1,0,1],# 提取豎邊的卷積核
[-1,0,1],
[-1,0,1]])
kernel_row = np.array([[-1,-1,-1],# 提取橫邊的卷積核
[0,0,0],
[1,1,1]])
kernel_incline1 = np.array([[2, 1, 0],# 提取橫邊的卷積核
[1, 0, -1],
[0, -1, -2]])
kernel_incline2 = np.array([[0, 1, 2],# 提取橫邊的卷積核
[-1, 0, 1],
[-2, -1, 0]])
dst_1 = cv.filter2D(img_rice, -1, kernel_incline1)
dst_2 = cv.filter2D(img_rice, -1, kernel_incline2)
dst_3 = cv.filter2D(img_rice, -1, kernel_row)
dst_4 = cv.filter2D(img_rice, -1, kernel_col)
dst_tmp1=np.maximum(dst_1,dst_2)
dst_tmp2=np.maximum(dst_3,dst_4)
dst_tmp=np.maximum(dst_tmp1,dst_tmp2)
# cv.imshow('incline_convolve',dst_tmp)
# cv.waitKey(0)
plt.subplot(2,2,2)
plt.imshow(dst_tmp,cmap='gray')

影像二值化方法
全域迭代法
# 計算影像的二值化閾值
def Iteration(img):
img_array = np.array(img).astype(np.float32)#轉化成陣列
I=img_array
zmax=np.max(I)
zmin=np.min(I)
mean=(zmax+zmin)/2#設定初始閾值
#根據閾值將影像進行分割為前景和背景,分別求出兩者的平均灰度 mean_fore和mean_back
b=1
m,n=I.shape
while b==0:
num_fore=0
num_back=0
fnum=0
bnum=0
for i in range(1,m):
for j in range(1,n):
tmp=I(i,j)
if tmp>=mean:
num_fore+num_fore+1
fnum=fnum+int(tmp) #前景像素的個數以及像素值的總和
else:
num_back=num_back+1
bnum=bnum+int(tmp)#背景像素的個數以及像素值的總和
#計算前景和背景的平均值
mean_fore=int(fnum/num_fore)
mean_back=int(bnum/num_back)
if mean==int((mean_fore+mean_back)/2):
b=0
else:
mean=int((mean_fore+mean_back)/2)
return mean
# 讀取車牌照片
img=cv.imread('car_image/1.JPG')
# 顏色空間轉換
img = cv.cvtColor(img,cv.COLOR_BGR2RGB)
gray = cv.cvtColor(img,cv.COLOR_RGB2GRAY)
img = cv.resize(gray,(300,200))#大小
Binar=Iteration(img)
# 二值化
thres, img_binar = cv.threshold(img, Binar, 255, cv.THRESH_BINARY)
print('threshold: ',thres)
plt.imshow(img_binar,cmap=cm.gray)
threshold: 127.0

大津法
def OTSU(img_array):
'''
該函式回傳使得類間方差最大的灰度閾值
img_array: 格式為ndarray
'''
height = img_array.shape[0]
width = img_array.shape[1]
count_pixel = np.zeros(256)
# 統計不同灰度值的分布情況
for i in range(height):
for j in range(width):
count_pixel[int(img_array[i][j])] += 1
#繪制直方圖可以觀察像素的分布情況
fig = plt.figure()
ax = fig.add_subplot(111)
ax.bar(np.linspace(0, 255, 256), count_pixel)
ax.set_xlabel("pixels")
ax.set_ylabel("num")
plt.show()
max_variance = 0.0
best_thresold = 0
# 遍歷所有灰度值,選擇最佳閾值
for thresold in range(256):
n0 = count_pixel[:thresold].sum()# 小于閾值的個數
n1 = count_pixel[thresold:].sum()# 大于閾值的個數
# 屬于前景的像素點數占整幅影像的比例
w0 = n0 / (height * width)
# 屬于背景的像素點數占整幅影像的比例
w1 = n1 / (height * width)
u0 = 0.0# 前景平均灰度
u1 = 0.0# 背景平均灰度
for i in range(thresold):
u0 += i * count_pixel[i]
for j in range(thresold, 256):
u1 += j * count_pixel[j]
# 影像總平均灰度
u = u0 * w0 + u1 * w1
# 類間方差
tmp_var = w0 * np.power((u - u0), 2) + w1 * np.power((u - u1), 2)
if tmp_var > max_variance:
best_thresold = thresold
max_variance = tmp_var
return best_thresold
# 讀取車牌照片
img=cv.imread('car_image/1.JPG')
# 顏色空間轉換
img = cv.cvtColor(img,cv.COLOR_BGR2RGB)
gray = cv.cvtColor(img,cv.COLOR_RGB2GRAY)
img = cv.resize(gray,(300,200))#大小
Binar=OTSU(img)
# 二值化
thres, img_binar = cv.threshold(img, Binar, 255, cv.THRESH_BINARY)
print('threshold: ',thres)
plt.imshow(img_binar,cmap=cm.gray)


獲取影像中的輪廓,對影像中的目標進行計數
原始圖片
plt.figure(figsize=[5,5])
plt.imshow(img_rice,cmap='gray')
plt.title('origin image')

使用區域閾值的大津演算法進行影像二值化
dst = cv.adaptiveThreshold(img_rice,255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY,101, 1)
element = cv.getStructuringElement(cv.MORPH_CROSS,(3, 3))#形態學去噪
dst=cv.morphologyEx(dst,cv.MORPH_OPEN,element) #開運算去噪
plt.imshow(dst,cmap='gray')

輪廓檢測函式
contours, hierarchy = cv.findContours(dst,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
cv.drawContours(dst,contours,-1,(120,0,0),2) #繪制輪廓
count=0 #米粒總數
ares_avrg=0 #米粒平均
img=dst
#遍歷找到的所有米粒
for cont in contours:
ares = cv.contourArea(cont)#計算包圍性狀的面積
if ares<50: #過濾面積小于10的形狀
continue
count+=1 #總體計數加1
ares_avrg+=ares
print("{}-blob:{}".format(count,ares),end=" ") #列印出每個米粒的面積
rect = cv.boundingRect(cont) #提取矩形坐標
print("x:{} y:{}".format(rect[0],rect[1]))#列印坐標
cv.rectangle(img,rect,(0xFF, 0xFF, 0xFF),1)#繪制矩形
y=10 if rect[1]<10 else rect[1] #防止編號到圖片之外
cv.putText(img,str(count), (rect[0], y), cv.FONT_HERSHEY_COMPLEX, 0.4, (255, 160, 180), 1) #在米粒左上角寫上編號
print("米粒平均面積:{}".format(round(ares_avrg/ares,2))) #列印出每個米粒的面積
cv.namedWindow("origin", 2) #創建一個視窗
cv.imshow('origin', img_rice) #顯示原始圖片
cv.namedWindow("dst", 2) #創建一個視窗
cv.imshow("dst", img) #顯示灰度圖
plt.hist(gray.ravel(), 256, [0, 256]) #計算灰度直方圖
plt.show()
cv.waitKey(0)
迭代結果

灰度值分布的直方圖

對識別出的目標進行標記

參考blog
- blog1
- blog2
- blog3
- blog4
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/300955.html
標籤:AI
上一篇:人工智能的常用十種演算法
