opencv-6 边缘检测(Prewitt算子,Sobel算子,Laplacian算子)

opencv-6 边缘检测(Prewitt算子,Sobel算子,Laplacian算子)_第1张图片

Roberts

filter2D形式实现


import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('lena.jpg')
lenna_img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
grayImage = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)


kernelx = np.array([[-1,0],[0,1]],dtype=int)
kernely = np.array([[0,-1],[1,0]],dtype=int)
x = cv2.filter2D(grayImage,cv2.CV_16S,kernelx) # cv2.CV_165 表示目标图像所需的深度
y = cv2.filter2D(grayImage,cv2.CV_16S,kernely)


absX = cv2.convertScaleAbs(x)#计算绝对值
absY = cv2.convertScaleAbs(y)
Roberts = cv2.addWeighted(absX,0.5,absY,0.5,0)


plt.rcParams['font.sans-serif'] = ['SimHei'] #使用参数字典 rcParams 访问并修改已经加载的配置项。
titles = [u'原始图像',u'Roberts算子']
images = [lenna_img,Roberts]

for i in range(2):
    plt.subplot(1, 2, i+1), plt.imshow(images[i],'gray')
    plt.title(titles[i])
    plt.xticks([]),   plt.yticks([])

plt.show()

Roberts

矩阵相乘实现

import cv2
from pylab import *

def RobertsAlogrithm(image):
    image = cv2.copyMakeBorder(image,1,1,1,1,cv2.BORDER_DEFAULT)
    # 如果你想给你的图片设置边界框,就像一个相框一样的东西,你就可以使用cv2.copyMakeBorder()函数。但其在卷积操作、零填充等也得到了应用,并且可以用于一些数据增广操作。
    # 添加的边框像素将是边界元素的镜面反射 类似于gfedcb|abcdefgh|gfedcba
    # print('大小是: ',image.shape)  #(202,202)
    for i in range(1,image.shape[0]):
        for j in range(1,image.shape[1]):
            image[i,j] = RobertsOperator(image[i-1:i+2,j-1:j+2]) # i-1 , i , i+1
    return image[ 1:image.shape[0], 1:image.shape[1] ]


def RobertsOperator(roi):
    operator_first = np.array([[-1,0],[0,1]])
    operator_second = np.array([[0,-1],[1,0]])
    x = roi[1:,1:]*operator_first
    y = roi[1:,1:]*operator_second
    return np.abs(np.sum(x))+np.abs(np.sum(y))
#np.sum() sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue)
#1.None,2.整数, 3.整数元组。(在默认/缺省的情况下,axis取None)
#如果axis取None,即将数组/矩阵中的元素全部加起来,得到一个和。
'''
a = np.linspace(1,20,20).reshape(4,5)
print(a)

输出:
[[ 1.  2.  3.  4.  5.]
 [ 6.  7.  8.  9. 10.]
 [11. 12. 13. 14. 15.]
 [16. 17. 18. 19. 20.]]
 
如果axis默认/缺失

np.sum(a)
即将数组/矩阵中的元素全部加起来,得到一个和:210

如果axis为整数,axis的取值不可大于数组/矩阵的维度,且axis的不同取值会产生不同的结果。

np.sum(a,axis = 0)
axis为0是压缩行,即将每一列的元素相加,将矩阵压缩为一行,输出:array([34., 38., 42., 46., 50.])

np.sum(a,axis = 1)
axis为1是压缩列,即将每一行的元素相加,将矩阵压缩为一列,输出:array([15., 40., 65., 90.])
https://zhuanlan.zhihu.com/p/85790648

'''


saber = cv2.imread('construction.jpg')
gray_saber = cv2.cvtColor(saber,cv2.COLOR_BGR2GRAY)#转成灰度图
gray_saber = cv2.resize(gray_saber,(200,200))#调整大小



Robert_saber = RobertsAlogrithm(gray_saber)

plt.imshow(Robert_saber,cmap='binary')
plt.axis('off')
plt.show()


prewitt算子

opencv-6 边缘检测(Prewitt算子,Sobel算子,Laplacian算子)_第2张图片

  Prewitt算子是一种一阶微分算子的边缘检测,利用像素点上下、左右邻点的灰度差,在边缘处达到极值检测边缘,去掉部分伪边缘,对噪声具有平滑作用 。其原理是在图像空间利用两个方向模板与图像进行邻域卷积来完成的,这两个方向模板一个检测水平边缘,一个检测垂直边缘。 

prewitt算子是加权平均算子,对噪声有抑制作用,但是像素平均相当于对图像进行的同滤波,所以prewitt算子对边缘的定位不如robert算子。

cv2.filter2D实现

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('lena.jpg')
lenna_img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)

grayImage = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

kernelx = np.array([[1,1,1],[0,0,0],[-1,-1,-1]],dtype=int)
kernely = np.array([[-1,0,1],[-1,0,1],[-1,0,1]],dtype=int)
x = cv2.filter2D(grayImage,cv2.CV_16S,kernelx)
y = cv2.filter2D(grayImage,cv2.CV_16S,kernely)

absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Prewitt = cv2.addWeighted(absX,0.5,absY,0.5,0)

plt.rcParams['font.sans-serif'] = ['SimHei']

titles = [u'原始图呀',u'Prewitt算子']
images = [lenna_img,Prewitt]
for i in range(2):
    plt.subplot(1,2,i+1)
    plt.imshow(images[i],'gray')
    plt.title(titles[i])
    plt.xticks([])
    plt.yticks([])
plt.show()

prewitt算子

矩阵相乘实现

import cv2
from pylab import *
import numpy as np

saber = cv2.imread('construction.jpg')
gray_saber = cv2.cvtColor(saber,cv2.COLOR_BGR2GRAY)
gray_saber = cv2.resize(gray_saber,(200,200))

def PreWittOperator(roi,operator_type):
    if operator_type =='horizontal':
        prewitt_operator = np.array([[-1,-1,-1],[0,0,0],[1,1,1]])
    elif operator_type == 'vertical':
        prewitt_operator = np.array([[-1,0,1],[-1,0,1],[-1,0,1]])
    else:
        raise('type Error')
    result = np.abs(np.sum(roi * prewitt_operator))
    return result

def PrewittAlogrithm(image,operator_type):
    new_image = np.zeros(image.shape)
    image = cv2.copyMakeBorder(image,1,1,1,1,cv2.BORDER_DEFAULT)
    for i in range(1,image.shape[0]-1 ):
        for j in range(1,image.shape[1]-1):
            new_image[i-1,j-1] = PreWittOperator(image[i-1:i+2,j-1:j+2],operator_type) #9宫格 处理完的赋值到new_image的左上角
    new_image = new_image*(255/np.max(image))  #这个不明白啥意思
    return new_image.astype(np.uint8) #astype转换数据类型


plt.subplot(121)#一行两列,第一张图
plt.title("horizontal")
plt.imshow(PrewittAlogrithm(gray_saber,'horizontal'),cmap='binary')
plt.axis('off')
plt.subplot(122)#一行两列,第二张图
plt.title('vertical')
plt.imshow(PrewittAlogrithm(gray_saber,'vertical'),cmap='binary')
plt.axis('off')
plt.show()

prewitt

加入噪声,以后补一下

sobel

简单实现(效果拉跨)

Sobel 算子根据像素点上下,左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘,对噪声具有平滑作用,提供较为精确的边缘方向信息。因为Sobel算子结合了高斯平滑和微分求导(分化),因此结果会具有更多的抗噪性,当对精度要求不是很高时,Sobel 算子是一种较为常用的边缘 检测方法。

由于该算子中引入了类似局部平均的运算,因此对噪声具有平滑作用,能很好的消除噪声的影响。

Sobel算子是滤波算子的形式来提取边缘,X,Y方向各用一个模板,两个模板组合起来构成一个梯度算子。X方向模板对垂直边缘影响最大,Y方向模板对水平边缘影响最大。

缺点是Sobel算子并没有将图像的主题与背景严格地区分开来,换言之就是Sobel算子并没有基于图像灰度进行处理,由于Sobel算子并没有严格地模拟人的视觉生理特征,所以提取的图像轮廓有时并不能令人满意。 

opencv-6 边缘检测(Prewitt算子,Sobel算子,Laplacian算子)_第3张图片

img = cv2.imread("lena.jpg",0)

a = cv2.Sobel(img,cv2.CV_16S,1,1)
absa = cv2.convertScaleAbs(a)

cv2.imshow("absa",absa)
cv2.waitKey()

opencv-6 边缘检测(Prewitt算子,Sobel算子,Laplacian算子)_第4张图片

水平竖直分别求完再求和(效果较好)

import  cv2
import numpy as np

img = cv2.imread("lena.jpg",0)

x = cv2.Sobel(img,cv2.CV_16S,1,0)
y = cv2.Sobel(img,cv2.CV_16S,0,1)

absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)

dst = cv2.addWeighted(absX,0.5,absY,0.5,0)

cv2.imshow("absX",absX)
cv2.imshow("absY",absY)

cv2.imshow("Result",dst)
cv2.waitKey()



在Sobel函数的第二个参数这里使用了cv2.CV_16S。因为OpenCV文档中对Sobel算子的介绍中有这么一句:“in the case of 8-bit input images it will result in truncated derivatives”。即Sobel函数求完导数后会有负值,还有会大于255的值。而原图像是uint8,即8位无符号数,所以Sobel建立的图像位数不够,会有截断。因此要使用16位有符号的数据类型,即cv2.CV_16S。

  在经过处理后,别忘了用convertScaleAbs()函数将其转回原来的uint8形式。否则将无法显示图像,而只是一副灰色的窗口。

 opencv-6 边缘检测(Prewitt算子,Sobel算子,Laplacian算子)_第5张图片opencv-6 边缘检测(Prewitt算子,Sobel算子,Laplacian算子)_第6张图片

 opencv-6 边缘检测(Prewitt算子,Sobel算子,Laplacian算子)_第7张图片

 absx那个 在竖直方向比较明显,看鼻子就看出来了

absy在水平方向识别的更好,看眉毛眼睛嘴唇

 两者经过 addweight 之后效果更好。

Laplacian 算子

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('lena.jpg')
lenna_img  = cv2.cvtColor(img , cv2.COLOR_BGR2RGB)
gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

dst = cv2.Laplacian(gray_img,cv2.CV_16S,ksize=3)
Laplacian = cv2.convertScaleAbs(dst)

plt.rcParams['font.sans-serif'] = ['SimHei']

titles = [u'原图',u'Laplacian算子']
images = [lenna_img,Laplacian]
for i in range(2):
    plt.subplot(1,2,i+1)
    plt.imshow(images[i],'gray')
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])

plt.show()

opencv-6 边缘检测(Prewitt算子,Sobel算子,Laplacian算子)_第8张图片

LOG (高斯拉普拉斯函数)

常见的LOG算子是5*5模板,如下图所示:

opencv-6 边缘检测(Prewitt算子,Sobel算子,Laplacian算子)_第9张图片

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('lena.jpg')
rgb_img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

gaussian = cv2.GaussianBlur(gray_img,(3,3),0)

dst = cv2.Laplacian(gaussian,cv2.CV_16S,ksize=3)
LOG = cv2.convertScaleAbs(dst)



plt.rcParams['font.sans-serif'] = ['SimHei']
titles = [u'原图',u'LOG']
images = [rgb_img,LOG]

for i in range(2):
    plt.subplot(1,2,i+1)
    plt.imshow(images[i],'gray')
    plt.title(titles[i])
    plt.xticks([])
    plt.yticks([])
plt.show()

你可能感兴趣的:(python,opencv)