python计算机视觉2:图像边缘检测
我是一名初學者,如果你發現文中有錯誤,請留言告訴我,謝謝
?
如果需要檢測到圖像里面的邊緣,首先我們需要知道邊緣處具有什么特征。
對于一幅灰度圖像來說,邊緣兩邊的灰度值肯定不相同,這樣我們才能分辨出哪里是邊緣,哪里不是。
因此,如果我們需要檢測一個灰度圖像的邊緣,我們需要找出哪里的灰度變化最大。顯然,灰度變化越大,對比度越強,邊緣就越明顯。
那么問題來了,我們怎么知道哪里灰度變化大,哪里灰度變化小呢?
?導數,梯度,邊緣信息?
在數學中,與變化率有關的就是導數。
如果灰度圖像的像素是連續的(實際不是),那么我們可以分別原圖像G對x方向和y方向求導數
,
獲得x方向的導數圖像Gx和y方向的導數圖像Gy。Gx和Gy分別隱含了x和y方向的灰度變化信息,也就隱含了邊緣信息。
如果要在同一圖像上包含兩個方向的邊緣信息,我們可以用到梯度。(梯度是一個向量)
原圖像的梯度向量Gxy為(Gx,Gy),梯度向量的大小和方向可以用下面兩個式子計算
角度值好像需要根據向量所在象限不同適當+pi或者-pi。
梯度向量大小就包含了x方向和y方向的邊緣信息。
?
?圖像導數?
實際上,圖像矩陣是離散的。
連續函數求變化率用的是導數,而離散函數求變化率用的是差分。
差分的概念很容易理解,就是用相鄰兩個數的差來表示變化率。
下面公式是向后差分
x方向的差分:Gx(n,y) = G(n,y)-G(n-1,y)
y方向的差分:Gy(x,n) = G(x,n)-G(x,n-1)
?實際計算圖像導數時,我們是通過原圖像和一個算子進行卷積來完成的(這種方法是求圖像的近似導數)。
最簡單的求圖像導數的算子是 Prewitt算子 :
x方向的Prewitt算子為
y方向的Prewitt算子為
---------------------------------------------
原圖像和一個算子進行卷積的大概過程如下
如果圖像矩陣中一塊區域為
那么x5處的x方向的導數是,將x方向算子的中心和x5重合,然后對應元素相乘再求和,即
x5處的x方向導數為x3+x6+x9-x1-x4-x7
對矩陣中所有元素進行上述計算,就是卷積的過程。
--------------------------------------------
因此,利用原圖像和x方向Prewitt算子進行卷積就可以得到圖像的x方向導數矩陣Gx,
利用原圖像和y方向Prewitt算子進行卷積就可以得到圖像的y方向導數矩陣Gy。
利用公式
就可以得到圖像的梯度矩陣Gxy,這個矩陣包含圖像x方向和y方向的邊緣信息。
?
?Python實現卷積及Prewitt算子的邊緣檢測?
?首先我們把圖像卷積函數封裝在一個名為imconv的函數中 ?( 實際上,scipy庫中的signal模塊含有一個二維卷積的方法convolve2d() ?)
import numpy as np from PIL import Imagedef imconv(image_array,suanzi):'''計算卷積參數image_array 原灰度圖像矩陣suanzi 算子返回原圖像與算子卷積后的結果矩陣'''image = image_array.copy() # 原圖像矩陣的深拷貝 dim1,dim2 = image.shape# 對每個元素與算子進行乘積再求和(忽略最外圈邊框像素)for i in range(1,dim1-1):for j in range(1,dim2-1):image[i,j] = (image_array[(i-1):(i+2),(j-1):(j+2)]*suanzi).sum()# 由于卷積后灰度值不一定在0-255之間,統一化成0-255image = image*(255.0/image.max())# 返回結果矩陣return image?
然后我們利用Prewitt算子計算x方向導數矩陣Gx,y方向導數矩陣Gy,和梯度矩陣Gxy。
import numpy as np import matplotlib.pyplot as plt# x方向的Prewitt算子 suanzi_x = np.array([[-1, 0, 1],[ -1, 0, 1],[ -1, 0, 1]])# y方向的Prewitt算子 suanzi_y = np.array([[-1,-1,-1],[ 0, 0, 0],[ 1, 1, 1]])# 打開圖像并轉化成灰度圖像 image = Image.open("pika.jpg").convert("L")# 轉化成圖像矩陣 image_array = np.array(image)# 得到x方向矩陣 image_x = imconv(image_array,suanzi_x)# 得到y方向矩陣 image_y = imconv(image_array,suanzi_y)# 得到梯度矩陣 image_xy = np.sqrt(image_x**2+image_y**2) # 梯度矩陣統一到0-255 image_xy = (255.0/image_xy.max())*image_xy# 繪出圖像 plt.subplot(2,2,1) plt.imshow(image_array,cmap=cm.gray) plt.axis("off") plt.subplot(2,2,2) plt.imshow(image_x,cmap=cm.gray) plt.axis("off") plt.subplot(2,2,3) plt.imshow(image_y,cmap=cm.gray) plt.axis("off") plt.subplot(2,2,4) plt.imshow(image_xy,cmap=cm.gray) plt.axis("off") plt.show()?
?Prewitt算子 的結果如下圖所示
上方:左圖為原圖像,右圖為x方向導數圖像
下方:左圖為y方向導數圖像,右圖為梯度圖像
從圖中可以看出,Prewitt算子雖然能檢測出圖像邊緣,但是檢測結果較為粗糙,還帶有大量的噪聲。
?
?近似導數的Sobel算子?
Sobel算子與Prewitt比較類似,但是它比Prewitt算子要好一些。
x方向的Sobel算子為
y方向的Sobel算子為
python代碼只需要將上面代碼中的Prewitt算子改成Sobel算子即可。
# x方向的Sobel算子 suanzi_x = np.array([[-1, 0, 1],[ -2, 0, 2],[ -1, 0, 1]])# y方向的Sobel算子 suanzi_y = np.array([[-1,-2,-1],[ 0, 0, 0],[ 1, 2, 1]])?Sobel算子?的結果如下圖所示
上方:左圖為原圖像,右圖為x方向導數圖像
下方:左圖為y方向導數圖像,右圖為梯度圖像
從圖中看出,比較Prewitt算子和Sobel算子,Sobel算子稍微減少了一點噪聲,但噪聲還是比較多的。
?近似二階導數的Laplace算子?
Laplace算子是一個二階導數的算子,它實際上是一個x方向二階導數和y方向二階導數的和的近似求導算子。
實際上,Laplace算子是通過Sobel算子推導出來的。
Laplace算子為
Laplace還有一種擴展算子為
為了不再重復造輪子,這次我們運用scipy庫中signal模塊的convolve()方法來計算圖像卷積。
convolve()的第一個參數是原圖像矩陣,第二個參數為卷積算子,然后指定關鍵字參數mode="same"(輸出矩陣大小和原圖像矩陣相同)。
import numpy as np from PIL import Image import matplotlib.pyplot as plt import matplotlib.cm as cm import scipy.signal as signal # 導入sicpy的signal模塊# Laplace算子 suanzi1 = np.array([[0, 1, 0], [1,-4, 1],[0, 1, 0]])# Laplace擴展算子 suanzi2 = np.array([[1, 1, 1],[1,-8, 1],[1, 1, 1]])# 打開圖像并轉化成灰度圖像 image = Image.open("pika.jpg").convert("L") image_array = np.array(image)# 利用signal的convolve計算卷積 image_suanzi1 = signal.convolve2d(image_array,suanzi1,mode="same") image_suanzi2 = signal.convolve2d(image_array,suanzi2,mode="same")# 將卷積結果轉化成0~255 image_suanzi1 = (image_suanzi1/float(image_suanzi1.max()))*255 image_suanzi2 = (image_suanzi2/float(image_suanzi2.max()))*255# 為了使看清邊緣檢測結果,將大于灰度平均值的灰度變成255(白色) image_suanzi1[image_suanzi1>image_suanzi1.mean()] = 255 image_suanzi2[image_suanzi2>image_suanzi2.mean()] = 255# 顯示圖像 plt.subplot(2,1,1) plt.imshow(image_array,cmap=cm.gray) plt.axis("off") plt.subplot(2,2,3) plt.imshow(image_suanzi1,cmap=cm.gray) plt.axis("off") plt.subplot(2,2,4) plt.imshow(image_suanzi2,cmap=cm.gray) plt.axis("off") plt.show()?
結果如下圖
其中上方為原圖像
下方:左邊為Laplace算子結果,右邊為Laplace擴展算子結果
從結果可以看出,laplace算子似乎比前面兩個算子(prewitt算子和Sobel算子)要好一些,噪聲減少了,但還是比較多。
而Laplace擴展算子的結果看上去比Laplace的結果少一些噪聲。
?降噪后進行邊緣檢測?
?為了獲得更好的邊緣檢測效果,可以先對圖像進行模糊平滑處理,目的是去除圖像中的高頻噪聲。
python程序如下
首先用標準差為5的5*5高斯算子對圖像進行平滑處理,然后利用Laplace的擴展算子對圖像進行邊緣檢測。
import numpy as np from PIL import Image import matplotlib.pyplot as plt import matplotlib.cm as cm import scipy.signal as signal# 生成高斯算子的函數 def func(x,y,sigma=1):return 100*(1/(2*np.pi*sigma))*np.exp(-((x-2)**2+(y-2)**2)/(2.0*sigma**2))# 生成標準差為5的5*5高斯算子 suanzi1 = np.fromfunction(func,(5,5),sigma=5)# Laplace擴展算子 suanzi2 = np.array([[1, 1, 1],[1,-8, 1],[1, 1, 1]])# 打開圖像并轉化成灰度圖像 image = Image.open("pika.jpg").convert("L") image_array = np.array(image)# 利用生成的高斯算子與原圖像進行卷積對圖像進行平滑處理 image_blur = signal.convolve2d(image_array, suanzi1, mode="same")# 對平滑后的圖像進行邊緣檢測 image2 = signal.convolve2d(image_blur, suanzi2, mode="same")# 結果轉化到0-255 image2 = (image2/float(image2.max()))*255# 將大于灰度平均值的灰度值變成255(白色),便于觀察邊緣 image2[image2>image2.mean()] = 255# 顯示圖像 plt.subplot(2,1,1) plt.imshow(image_array,cmap=cm.gray) plt.axis("off") plt.subplot(2,1,2) plt.imshow(image2,cmap=cm.gray) plt.axis("off") plt.show()?
結果如下圖
從圖中可以看出,經過降噪處理后,邊緣效果較為明顯。
?
參考列表
1. 《python計算機視覺編程》?
2. 網絡(感謝百度,感覺網絡上分享知識的網友)
實際上,一些現成的Python庫已經對邊緣檢測過程進行了封裝,效果和效率更為出色。
文中以自己的python代碼進行邊緣檢測,實際上是想對實際過程有更好的認識和了解
?
轉載于:https://www.cnblogs.com/smallpi/p/4555854.html
總結
以上是生活随笔為你收集整理的python计算机视觉2:图像边缘检测的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简化Java中的异常处理
- 下一篇: java对象和json对象之间互相转换