目录

前言

一、边缘检测算法

1、一阶算子

2、二阶算子

二、一阶算子

原图像lena

1、Roberts算子

不同方向的算子模板

梯度的计算

系统代码:

自定义函数代码

结果

2、Prewitt

不同方向的算子

梯度计算

系统自带代码

自定义函数代码

结果

3、Sobel

不同方向的算子

梯度计算

系统自带代码(opencv和skimage)

自定义函数代码

结果

4、 Kirsch

不同方向的算子

梯度计算

自定义函数代码

结果

5、Canny

canny主要计算步骤:

不同方向的算子

梯度计算

系统自带代码

自定义函数代码

结果

三、填充补零操作(究竟补多少行)

1、why&how

2、cv2.copyMakeBorder()

3、创建零图像矩阵

四、不同一阶算子的比较


前言

耐心看完一定会有收获的,大部分内容也会在代码中体现,结合理论知识和代码进行理解会更有效

一、边缘检测算法

边缘检测算法是指利用灰度值的不连续性质,以灰度突变为基础分割出目标区域。对铝铸件表面进行成像后会产生一些带缺陷的区域,这些区域的灰度值比较低,与背景图像相比在灰度上会有突变,这是由于这些区域对光线产生散射所引起的。因此边缘检测算子可以用来对特征的提取。

1、一阶算子

一种是基于一阶微分的算子,也称基于搜索的算子,首先通过一阶导数计算边缘强度,然后采用梯度的方向来对边缘的局部方向进行寻找,同时根据该方向来寻找出局部梯度模的最大值,由此定位边缘,如Roberts Cross算子,Prewitt算子Sobel算子,Kirsch算子,Canny算子,罗盘算子等;

图像中的边缘区域,像素值会发生“跳跃”,对这些像素求导,在其一阶导数在边缘位置为极值,这就是Sobel算子使用的原理——极值处就是边缘。

2、二阶算子

另一种是基于二阶微分的算子,也称基于零交叉的算子,通过寻找由图像得到的二阶导数的过零点来定位检测边缘,如Marr-Hildreth算子,Laplacian算子,LOG算子等

二阶算子可见:《【图像处理】——Python图像分割边缘检测算法之二阶梯度算子(laplace、log、dog算子)》

如果对像素值求二阶导数,会发现边缘处的导数值为0

二、一阶算子

参考:《一阶算子opencv中函数的参数解读与举例》

一阶微分算子进行边缘检测的思路大致就是通过指定大小的核(kernal)(也称为算子)与图像进行卷积,将得到的梯度进行平方和或者最大值作为新的梯度赋值给对应的像素点,不同的一阶微分算子主要的不同在于其算子即核的元素不同以及核的大小不一样

以下是连续函数的一阶导数求导公式因为图像是一个面,就相当于是灰度值关于x,y两个方向的函数,要求某一点的导数,则是各个方向的偏导数的平方和再进行开方运算

离散函数的一阶导数公式:

y'=[y(x0+h)-y(x0-h)]/(2h);这是一维函数的一阶求导,h是步长,在图像处理中一般为1

首先复习一下什么是卷积?

卷积就是对应的元素相乘再进行累加的过程

原图像lena

1、Roberts算子

Robert算子是用于求解对角线方向的梯度,因为根据算子GX和GY的元素设置可以看到,只有对角线上的元素非零

不同方向的算子模板

梯度的计算

系统代码:

def sys_robert(img):'''直接调用系统skimage模块给的函数:param img: 待处理图片:return: 返回的是边缘图像矩阵'''gray = cv2.imread(img,0)#读取的时候直接将其读取成灰色的图片,变成一个二维灰度值矩阵edge_img = filters.roberts(gray)#进行边缘检测,得到边缘图像return edge_img

自定义函数代码

其中对于下列函数def_col可以直接利用cv2.filter2D(灰度图,-1表示与原图一样的size,核)

def def_col(gray,kernal):'''对指定的图像用指定的核对齐其进行卷积,得到卷积后的图像:param gray: 补零操作后的灰度图像:param kernal: 卷积核:return: 返回卷积后的图像,即边缘图像'''edge_img = cv2.filter2D(gray, -1, kernal)#上面这句和下面的代码有一样的效果# h,w = gray.shape# edge_img = np.zeros((h-1,w-1))#用于存放卷积后的边缘图像矩阵# for i in range(h-1):#     for j in range(w-1):#         #用卷积核与图像上对应大小的块进行卷积,这里是以左上角的格子为基准进行的卷积#         edge_img[i,j]=gray[i,j]*kernal[0,0]+gray[i,j+1]*kernal[0,1]+gray[i+1,j]*kernal[1,0]+gray[i,j]*kernal[1,1]return edge_imgdef def_robert(img):'''对指定的图像进行边缘检测:param img: 要检测的图像:return: 返回边缘图像'''gray = cv2.imread(img,0)# np.savetxt("result2.txt", gray, fmt="%d")h = gray.shape[0]w = gray.shape[1]#定义Robert算子的卷积核,这个是对角线方向x_kernal = np.array([[1,0],[0,-1]])y_kernal = np.array([[0,1],[-1,0]])#由于卷积核和图像进行卷积是以右下角的像素进行定位卷积核和目标像素点的位置关系,因此为了能够遍历整个图像,#需要在图像的第一行和第一列进行补零操作gray_zero = np.zeros((h+1,w+1))#先生成一个(h+1,w+1)的零图像矩阵#将原始图像去填充零矩阵,第一行和第一列不填充for i in range(1,h+1):for j in range(1,w+1):gray_zero[i,j]=gray[i-1,j-1]gray = gray_zero#将填充后的矩阵复制给gray#通过卷积,得到x和y两个方向的边缘检测图像x_edge = def_col(gray,x_kernal)y_edge = def_col(gray,y_kernal)#创建一个与原始图像大小一致的空图像,用于存放经过Robert算子的边缘图像矩阵edge_img = np.zeros((h,w),np.uint8)#根据计算公式得到最终的像素点灰度值for i in range(h):for j in range(w):edge_img[i,j] = (np.sqrt(x_edge[i,j]**2+y_edge[i,j]**2))return edge_imgif __name__ == '__main__':img = "test1.png"edge = def_robert(img)edge1 = sys_robert(img)#以下是将看一下两种方法图像各点的像素值的比值,用c存放# h,w = edge1.shape# c = np.zeros((h,w))# c[0:h,0:w] = edge1[0:h,0:w]/edge1[0:h,0:w]# for i in range(h):#     for j in range(w):#         if edge1[i,j] != 0:#             c[i,j] = edge[i,j]/edge1[i,j]#         else:#             c[i,j] = edge[i,j]# print(max(c.ravel()))#进行numpy保存到TXT中的操作# np.savetxt("result.txt", edge, fmt="%d")# np.savetxt("c.txt", c)# np.savetxt("result1.txt", edge1)cv2.imshow('',edge)cv2.imshow('222',edge1)cv2.waitKey(0)

结果

2、Prewitt

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

不同方向的算子

梯度计算

用某点的x向梯度和y向梯度的最大值代表该点梯度;2.用x向梯度和y向梯度的和代替该点梯度;3.用x向梯度和y向梯度的平方根代替该点的梯度(同python自带的运算);4.用x向梯度和y向梯度绝对值的和代替该点的梯度

系统自带代码

from skimage import data,color,filters
import matplotlib.pyplot as plt
import numpy as np
import cv2def sys_prewitt(img):'''prewitt系统自带:param img: 原始图像:return: 返回边缘图像'''img = cv2.imread(img,0)edge_img=filters.prewitt(img)return edge_img

自定义函数代码

def def_col(img,kernal):'''对指定的图像用指定的核对齐其进行卷积,得到卷积后的图像:param img: 补零操作后的灰度图像:param kernal: 卷积核:return: 返回卷积后的图像,即边缘图像'''edge_img = cv2.filter2D(img,-1,kernal)# h,w = img.shape# edge_img=np.zeros([h-2,w-2])# for i in range(h-2):#     for j in range(w-2):#         edge_img[i,j]=img[i,j]*kernal[0,0]+img[i,j+1]*kernal[0,1]+img[i,j+2]*kernal[0,2]+\#                 img[i+1,j]*kernal[1,0]+img[i+1,j+1]*kernal[1,1]+img[i+1,j+2]*kernal[1,2]+\#                 img[i+2,j]*kernal[2,0]+img[i+2,j+1]*kernal[2,1]+img[i+2,j+2]*kernal[2,2]return edge_imgdef def_prewitt(img,type_flags):gray = cv2.imread(img,0)h = gray.shape[0]w = gray.shape[1]x_prewitt=np.array([[1,0,-1],[1,0,-1],[1,0,-1]])y_prewitt=np.array([[1,1,1],[0,0,0],[-1,-1,-1]])img=np.zeros([h+2,w+2])img[2:h+2,2:w+2]=gray[0:h,0:w]edge_x_img=def_col(img,x_prewitt)edge_y_img=def_col(img,y_prewitt)#p(i,j)=max[edge_x_img,edge_y_img]这里是将x,y中最大的梯度来代替该点的梯度edge_img_max=np.zeros([h,w],np.uint8)for i in range(h):for j in range(w):if edge_x_img[i][j]>edge_y_img[i][j]:edge_img_max=edge_x_img[i][j]else:edge_img_max=edge_y_img#p(i,j)=edge_x_img+edge_y_img#将梯度和替代该点梯度edge_img_sum=np.zeros([h,w],np.uint8)for i in range(h):for j in range(w):edge_img_sum[i][j]=edge_x_img[i][j]+edge_y_img[i][j]# p(i,j)=|edge_x_img|+|edge_y_img|将绝对值的和作为梯度edge_img_abs = np.zeros([h, w],np.uint8)for i in range(h):for j in range(w):edge_img_abs[i][j] = abs(edge_x_img[i][j]) + abs(edge_y_img[i][j])#p(i,j)=sqrt(edge_x_img**2+edge_y_img**2)将平方和根作为梯度edge_img_sqrt=np.zeros([h,w],np.uint8)for i in range(h):for j in range(w):edge_img_sqrt[i][j]=np.sqrt((edge_x_img[i][j])**2+(edge_y_img[i][j])**2)type = [edge_img_max,edge_img_sum,edge_img_abs,edge_img_sqrt]return type[type_flags]if __name__ == '__main__':img = 'colorful_lena.jpg'edge = sys_prewitt(img)edge1 = def_prewitt(img,3)cv2.imshow('',edge)cv2.imshow('1',edge1)cv2.waitKey(0)

结果

3、Sobel

sobel和prewitt算子很像,都是检测水平和垂直边缘,但是其最终梯度的计算和算子的元素值不一样

不同方向的算子

梯度计算

系统自带代码(opencv和skimage)

import cv2
from skimage import filters,color
import numpy as npdef cv2_sobel(img):'''利用cv2自带的函数进行sobel边缘检测:param img: 待检测的图像:return: 返回不同方向梯度的边缘图像矩阵,通过控制不同方向的导数为1或者0来选择卷积核'''img = cv2.imread(img)#读取图片gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#灰度化sobel_x = cv2.Sobel(gray, cv2.CV_8U, 1, 0)#通过控制dx,dy的值来控制梯度的方向,这里是求x方向的梯度sobel_y = cv2.Sobel(gray, cv2.CV_8U, 0, 1)#这里是求y方向的梯度sobel = cv2.Sobel(gray, cv2.CV_8U, 1, 1)#这里是求x,y方向综合的梯度return [sobel_x,sobel_y,sobel]def skimage_sobel(img):'''利用skimage模块自带的函数进行图像的边缘检测:param img: 待检测图像:return: 返回的是边缘图像矩阵'''gray = cv2.imread(img,0)edge_img = filters.sobel(gray)#这里求的是x,y方向两个梯度的综合return edge_img

自定义函数代码

def sobel_cal(img, kernal):'''自定义sobel卷积函数:param img: 已经补零了的图像:param kernal: 指定方向的梯度算子,核:return: 返回梯度矩阵'''edge_img = cv2.filter2D(img, -1, kernal)# h, w = img.shape# img_filter = np.zeros([h, w])# #机芯卷积操作# for i in range(h - 2):#     for j in range(w - 2):#         img_filter[i][j] = img[i][j] *kernal[0][0] + img[i][j + 1] * kernal[0][1] + img[i][j + 2] * kernal[0][2] + \#                                img[i + 1][j] * kernal[1][0] + img[i + 1][j + 1] *kernal[1][1] + img[i + 1][j + 2] * kernal[1][2] + \#                                img[i + 2][j] * kernal[2][0] + img[i + 2][j + 1] * kernal[2][1] + img[i + 2][j + 2] * kernal[2][2]return edge_imgdef def_sobel(img):#定义不同方向的梯度算子x_sobel = np.array([[-1, 0, 1],[-2, 0, 2],[-1, 0, 1]])y_sobel = np.array([[-1, -2, -1],[0, 0, 0],[1, 2, 1]])gray_img = cv2.imread(img,0)h, w = gray_img.shapeimg = np.zeros([h + 2, w + 2],np.uint8)img[2:h + 2, 2:w + 2] = gray_img[0:h, 0:w]x_edge_img = sobel_cal(img, x_sobel)y_edge_img = sobel_cal(img, y_sobel)edge_img = np.zeros([h, w])for i in range(h):for j in range(w):edge_img[i][j] = np.sqrt(x_edge_img[i][j] ** 2 + y_edge_img[i][j] ** 2) / (np.sqrt(2))return edge_imgif __name__ == '__main__':img = "test1.png"sobel_x,sobel_y,sobel = cv2_sobel(img)sk_sobel = skimage_sobel(img)def_sobelimg = def_sobel(img)cv2.imshow("src", cv2.imread(img))cv2.imshow("Sobel_x", sobel_x)cv2.imshow("Sobel_y", sobel_y)cv2.imshow("Sobel", sobel)cv2.imshow("sk_Sobel", sk_sobel)cv2.imshow("def_Sobel", def_sobelimg)cv2.waitKey(0)cv2.destroyAllWindows()

结果

4、 Kirsch

不同方向的算子

梯度计算

Kirsch边缘算子由八个方向的卷积核构成,这8个模板代表8个方向,对图像上的8个特定边缘方向作出最大响应,运算中取最大值作为图像的边缘输出

自定义函数代码

from skimage import data,color
import matplotlib.pyplot as plt
import numpy as np
import cv2def conv_cal(img,kernal):edge_img = cv2.filter2D(img, -1, kernal)# h,w=img.shape# img_filter=np.zeros([h,w])# for i in range(h-2):#     for j in range(w-2):#         img_filter[i][j]=img[i][j]*filter[0][0]+img[i][j+1]*filter[0][1]+img[i][j+2]*filter[0][2]+\#                 img[i+1][j]*filter[1][0]+img[i+1][j+1]*filter[1][1]+img[i+1][j+2]*filter[1][2]+\#                 img[i+2][j]*filter[2][0]+img[i+2][j+1]*filter[2][1]+img[i+2][j+2]*filter[2][2]return edge_imgdef def_krisch(img):#定义算子krisch1=np.array([[5,5,5],[-3,0,-3],[-3,-3,-3]])krisch2=np.array([[-3,-3,-3],[-3,0,-3],[5,5,5]])krisch3=np.array([[5,-3,-3],[5,0,-3],[5,-3,-3]])krisch4=np.array([[-3,-3,5],[-3,0,5],[-3,-3,5]])krisch5=np.array([[-3,-3,-3],[-3,0,5],[-3,5,5]])krisch6=np.array([[-3,-3,-3],[5,0,-3],[5,5,-3]])krisch7=np.array([[-3,5,5],[-3,0,5],[-3,-3,-3]])krisch8=np.array([[5,5,-3],[5,0,-3],[-3,-3,-3]])gray_img = cv2.imread(img,0)w,h=gray_img.shapeimg=np.zeros([w+2,h+2])img[2:w+2,2:h+2]=gray_img[0:w,0:h]edge1=conv_cal(img,krisch1)edge2=conv_cal(img,krisch2)edge3=conv_cal(img,krisch3)edge4=conv_cal(img,krisch4)edge5=conv_cal(img,krisch5)edge6=conv_cal(img,krisch6)edge7=conv_cal(img,krisch7)edge8=conv_cal(img,krisch8)edge_img=np.zeros([w,h],np.uint8)for i in range(w):for j in range(h):edge_img[i][j]=max(list([edge1[i][j],edge2[i][j],edge3[i][j],edge4[i][j],edge5[i][j],edge6[i][j],edge7[i][j],edge8[i][j]]))return [edge1,edge2,edge3,edge4,edge5,edge6,edge7,edge8,edge_img]if __name__ == '__main__':img = 'colorful_lena.jpg'edge1, edge2, edge3, edge4, edge5, edge6, edge7, edge8,edge_img = def_krisch(img)cv2.imshow('1',edge1)cv2.imshow('2',edge2)cv2.imshow('3',edge3)cv2.imshow('4',edge4)cv2.imshow('5',edge5)cv2.imshow('6',edge6)cv2.imshow('7',edge7)cv2.imshow('8',edge8)cv2.imshow('9',edge_img)cv2.waitKey(0)

结果

这个结果我也看不懂。。。。

5、Canny

可以说,Canny 边缘检测算法是被业界公认的性能最为优良的边缘检测算法之一。Canny算法不是像Roberts、Prewitt、Sobel等这样简单梯度算子或锐化模板,它是在梯度算子基础上,引入了一种能获得抗噪性能好、定位精度高的单像素边缘的计算策略。
       Canny把边缘检测问题转化为检测单位函数极大值问题。在高斯噪声假设中,一个典型的边缘代表一个阶跃的强度变化

根据这个模型,一个好的边缘检测算子应满足以下3个指标:

(1) 低失误概率;(2) 高位置精度;(3) 对每个边缘有唯一的响应。
换句话说边缘检测问题就是要做到: 抑制噪声,精确定位边缘

canny主要计算步骤:

Canny 边缘检测算法的主要计算步骤包括四个方面:
(1) 利用高斯滤波器对原始图像进行平滑滤波,以提高算法的抗噪性。
(2) 用一阶有限差分近似代替偏导数计算图像梯度强度和方向。计算梯度可以利用先前的Roberts、 Prewitt、Sobel等算子(文中用的是Prewitt 算子)。其中,方向信息是为了下一步计算需要。
(3) 利用第(2)步梯度方向划分(划分为四个方向 0\ -45\ 90 \45 )进行梯度强度的非极大抑制,获取单像素边缘点。

个人认为比较难懂的就是第三步,实际上做的就是检测梯度方向是否垂直于切线方向,那么该怎么来确定这个切线方向呢?因为每个像素点的梯度方向在第二部中已经确定了,因此我们只需要取与梯度方向垂直的三个像素点即可,若该点的像素值是这三个中最大的,则说明这是梯度的切线方向处的极大值点,这就说明了这就是我们要找的边缘(边缘的灰度值突变性大,因此梯度大),下面用图表示了一下,可以结合代码进行查看


(4) 双(或滞后)阈值进行边缘的二值化

基本思路:通过梯度值和梯度方向划分后,得到一个只含有邻域为最大梯度的像素矩阵,这些点设为0,其余为255,然后再根据阈值对前面的像素矩阵进行修正设置,这样就得到了比较好的边缘图像

参考:https://blog.csdn.net/weixin_44403952/article/details/90375013

不同方向的算子

这里的算子是使用的是prewitt算子,也可以自定义一个合理的算子进行使用梯度计算

梯度计算

canny算法中的梯度在计算过程中会变化好多次:一个是由计算公式得到的梯度,类似于krewitt算子;一种是通过对第一种梯度进行梯度方向划分以及非极大值梯度抑制后得到的梯度,另一种就是梯度方向

系统自带代码

# coding=utf-8
import cv2
import numpy as npdef cv2_canny(img):'''由于Canny只能处理灰度图,所以将读取的图像转成灰度图。用高斯平滑处理原图像降噪。调用Canny函数,指定最大和最小阈值,其中apertureSize默认为3。:param img: :return: '''img = cv2.imread(img, 0)img = cv2.GaussianBlur(img, (3, 3), 2)edge_img = cv2.Canny(img, 50,100)return edge_img

自定义函数代码

def def_canny(img,top=1,buttom=1,left=1,right=1):'''自定义canny算子,这里可以修改的参数有填充的方式和值、高斯滤波是核的大小以及sigmaX的值:param img: 待测图片:param top: 图像上方填充的像素行数:param buttom: 图像下方填充的像素行数:param left: 图像左方填充的像素行数:param right: 图像右方填充的像素行数:return: [img1,img2,img3,theta]img1:梯度幅值图;img2:非极大值抑制梯度灰度图;img3:最终边缘图像;theta:梯度方向灰度图'''#算子,这里的梯度算子可以使用sobel、sobel等算子,这里用的是prewitt梯度算子m1 = np.array([[-1,0,1],[-1,0,1],[-1,0,1]])m2 = np.array([[-1,-1,-1],[0,0,0],[1,1,1]])# 第一步:完成高斯平滑滤波img = cv2.imread(img,0)#将彩色读成灰色图片img = cv2.GaussianBlur(img,(3,3),2)#高斯滤波# 第二步:完成一阶有限差分计算,计算每一点的梯度幅值与方向img1 = np.zeros(img.shape,dtype="uint8") # 与原图大小相同,用于存放梯度值theta = np.zeros(img.shape,dtype="float")  # 方向矩阵原图像大小,用于存放梯度方向img = cv2.copyMakeBorder(img,top,buttom,left,right,borderType=cv2.BORDER_CONSTANT,value=0)#表示增加的像素数,类似于补零操作,这里上下左右均增加了一行填充方式为cv2.BORDER_REPLICATErows,cols = img.shape#以下是进行了卷积,以核与图像的卷积来代替核中间对应的那个值#dst = cv2.filter2D(img, -1, kernel)这个函数虽然可以直接进行卷积,但是无法返回梯度方向for i in range(1,rows-1):for j in range(1,cols-1):# Gy,对应元素相乘再累加Gy = (np.dot(np.array([1, 1, 1]), (m1 * img[i - 1:i + 2, j - 1:j + 2]))).dot(np.array([[1], [1], [1]]))# Gx,对应元素相乘再累加Gx = (np.dot(np.array([1, 1, 1]), (m2 * img[i - 1:i + 2, j - 1:j + 2]))).dot(np.array([[1], [1], [1]]))#将所有的角度转换到-180到180之间if Gx[0] == 0:theta[i-1,j-1] = 90continueelse:temp = (np.arctan(Gy[0] / Gx[0]) ) * 180 / np.pi#求梯度方向if Gx[0]*Gy[0] > 0:if Gx[0] > 0:theta[i-1,j-1] = np.abs(temp)else:theta[i-1,j-1] = (np.abs(temp) - 180)if Gx[0] * Gy[0] < 0:if Gx[0] > 0:theta[i-1,j-1] = (-1) * np.abs(temp)else:theta[i-1,j-1] = 180 - np.abs(temp)img1[i-1,j-1] = (np.sqrt(Gx**2 + Gy**2))#求两个方向梯度的平方和根#以下对梯度方向进行了划分,将梯度全部划分为0,90,45,-45四个角度for i in range(1,rows - 2):for j in range(1, cols - 2):if (    ( (theta[i,j] >= -22.5) and (theta[i,j]< 22.5) ) or( (theta[i,j] <= -157.5) and (theta[i,j] >= -180) ) or( (theta[i,j] >= 157.5) and (theta[i,j] < 180) ) ):theta[i,j] = 0.0elif (    ( (theta[i,j] >= 22.5) and (theta[i,j]< 67.5) ) or( (theta[i,j] <= -112.5) and (theta[i,j] >= -157.5) ) ):theta[i,j] = 45.0elif (    ( (theta[i,j] >= 67.5) and (theta[i,j]< 112.5) ) or( (theta[i,j] <= -67.5) and (theta[i,j] >= -112.5) ) ):theta[i,j] = 90.0elif (    ( (theta[i,j] >= 112.5) and (theta[i,j]< 157.5) ) or( (theta[i,j] <= -22.5) and (theta[i,j] >= -67.5) ) ):theta[i,j] = -45.0# 第三步:进行 非极大值抑制计算#这里做的事情其实就是在寻找极值点,像素点的值是与该像素点的方向垂直方向上相邻三个像素最大的,则将该像素点定义为极值点,即边缘像素img2 = np.zeros(img1.shape) # 非极大值抑制图像矩阵for i in range(1,img2.shape[0]-1):for j in range(1,img2.shape[1]-1):if (theta[i,j] == 0.0) and (img1[i,j] == np.max([img1[i,j],img1[i+1,j],img1[i-1,j]]) ):img2[i,j] = img1[i,j]if (theta[i,j] == -45.0) and img1[i,j] == np.max([img1[i,j],img1[i-1,j-1],img1[i+1,j+1]]):img2[i,j] = img1[i,j]if (theta[i,j] == 90.0) and  img1[i,j] == np.max([img1[i,j],img1[i,j+1],img1[i,j-1]]):img2[i,j] = img1[i,j]if (theta[i,j] == 45.0) and img1[i,j] == np.max([img1[i,j],img1[i-1,j+1],img1[i+1,j-1]]):img2[i,j] = img1[i,j]# 第四步:双阈值检测和边缘连接img3 = np.zeros(img2.shape) #定义双阈值图像# TL = 0.4*np.max(img2)# TH = 0.5*np.max(img2)TL = 50#低阈值TH = 100#高阈值#关键在这两个阈值的选择#小于低阈值的则设置为0,大于高阈值的则设置为255for i in range(1,img3.shape[0]-1):for j in range(1,img3.shape[1]-1):#将比较明确的灰度级的像素点绘制出来if img2[i,j] < TL:img3[i,j] = 0elif img2[i,j] > TH:img3[i,j] = 255#如果一个像素点的值在二者之间,且这个像素点的邻近的八个像素点有一个的像素值是小于高阈值的,则将该点设置为255#实际上下面这段代码就是将剩余的像素点都设置成了255,这样就实现了边缘连接elif (( img2[i+1,j] < TH) or (img2[i-1,j] < TH )or( img2[i,j+1] < TH )or(img2[i,j-1] < TH) or (img2[i-1, j-1] < TH )or ( img2[i-1, j+1] < TH) or( img2[i+1, j+1] < TH ) or ( img2[i+1, j-1] < TH) ):img3[i,j] = 255return [img1,img2,img3,theta]if __name__ == '__main__':img = 'test1.png'img1, img2, img3, theta = def_canny(img)cv2.imshow("1", cv2.imread(img, 0))  # 原始图像cv2.imshow("2", img1)  # 梯度幅值图cv2.imshow("3", img2)  # 非极大值抑制灰度图cv2.imshow("4", img3)  # 最终效果图cv2.imshow("theta", theta)  # 角度值灰度图edge_img = cv2_canny(img)cv2.imshow("orign", cv2.imread(img))cv2.imshow('Canny', edge_img)cv2.waitKey(0)cv2.destroyAllWindows()

结果

三、填充补零操作(究竟补多少行)

1、why&how

这里我们要和卷积神经网络的卷积区分开来,卷积神经网络卷积的主要目的是将图片的信息进行压缩,因此经过卷积图片的尺寸会变小,而在我们这里边缘检测中,我们要保证图像的大小不发生改变,这时候我们就需要进行补零操作,具体补几行几列,还得看情况而定,一般是和核的大小有关,一般情况下假设核是nXn的,那么就需要补充n-1行和n-1列

注意这里不一定是补零,也可以是其他的填充方法,如复制边缘的像素值进行填充,出来的效果就是拖拉的感觉,

2、cv2.copyMakeBorder()

在opencv中提供了函数cv2.copyMakeBorder()进行填充

cv2.copyMakeBorder(src, top, bottom, left, right, borderType, dst=None, value=None)
cv2.copyMakeBorder(img, 1, 1, 1, 1, cv2.BORDER_DEFAULT)

上面代码就表示按照默认的填充方式在上下左右各填充一行像素

可参考:https://blog.csdn.net/zfjBIT/article/details/86655182

3、创建零图像矩阵

记住一定要将其类型设置为dtype = np.uint8,否则不太好,多次创建0矩阵图像时,只需要将最后一个进行设置即可

img3 = np.zeros(img2.shape,np.uint8)

四、不同一阶算子的比较

Roberts、Prewitt、Sobel等梯度算子不能获取单像素边缘,并进行必要的计算结果对比展示。实际上,以上简单的梯度算子与图像进行卷积滤波,仅仅是对图像的一个锐化过程,得到的仅是原始图像的梯度图,而不是最终的边缘结果,当然不会是单像素边缘。

梯度算子Prewitt和Sobel它们的计算过程比较简单,所以计算出来的结果精度也就较低,利用它们进行检测出来的图像轮 廓比较简单,对于那些细的边缘以及裂纹缺陷就有可能检测不出来;相比于梯度算子,在检测效果方面,LOG滤波算子以及Canny算子会更好,对于图像的那些细节的边缘特征它们也可以检测得出来。研究表明,与LOG算子相比,采用Canny算子出来的边缘检测效果比较好,同时噪声干扰也较少

【图像处理】——Python图像分割边缘检测算法之一阶梯度算子(Roberts、Prewitt、Sobel、 Kirsch、Canny算子)相关推荐

  1. 【图像处理】——Python图像分割边缘检测算法之二阶梯度算子(laplace、log、dog算子)

    目录 一.二阶算子简介 二.laplace(拉普拉斯算子) 1.什么是拉普拉斯算子 (1)连续二维函数的二阶导 (2)离散二维函数的二阶导数 2.常用算子模块及代码 3.结果 三.log算子 1.什么 ...

  2. 【图像处理】 常用边缘检测算法对比分析

    文章目录一瞥 边缘的定义 边缘检测的基本方法 图像滤波 图像增强 图像检测 图像定位 常见边缘检测算子分析 1) 差分边缘检测 2)Reborts算子 3)Sobel算子 4)Prewitt 算子 非 ...

  3. 图像算法八:【图像分割】边缘检测(Roberts,Sobel,canny)、霍夫变换(hough)、阈值分割、区域分割

    1.我们主要学习以下四种差分算子 Roberts算子 Sobel算子 Prewitt算子 canny算子 % Matlab图像边缘检测梯度算子–Roberts.Prewitt.Sobel.LOG.Ca ...

  4. 数字图像处理:图像分割 人工智能算法在图像处理中的应用

    人工智能算法在图像处理中的应用 人工智能算法包括遗传算法.蚁群算法.模拟退火算法和粒子群算法等,在图像边缘检测.图像分割.图像识别.图像匹配.图像分类等领域有广泛应用.本文首先介绍常用人工智能算法的的 ...

  5. 图像分割-基本边缘检测roberts,prewitt,sobel,canny,laplace

    执行边缘检测的三个基本步骤: 1.为降噪对图像进行平滑处理.(导数对噪声具有敏感性.图像的正负分量检测困难) 2.边缘点的检测.(提取边缘点的潜在候选者) 3.边缘定位.(从候选者中选出真是边缘点成员 ...

  6. Opencv(python)图像梯度和边缘检测算法

    1.图像梯度 图像梯度计算的是图像的边缘信息 ,图像梯度计算的是图像变化的速度.对于图像的边缘部分,其灰度值变化较大,梯度值也较大,对于图像中比较平滑的部分,其灰度值变化较小,相应的梯度值也较小.图像 ...

  7. 图像处理+边缘检测算法

    一.边缘检测算子类别 常见边缘检测算子:Roberts .Sobel .Prewitt.Laplacian.Log/Marr.Canny.Kirsch.Nevitia 二.一阶微分算子:Roberts ...

  8. 数字图像处理课程实习——边缘检测与图像分割

    本次实验的目标主要是为了掌握图像边缘检测和分割的意义和手段,掌握图像边缘检测和分割的方法及应用并通过实验讨论不同算子对边缘检测效果的影响. 本次实验主要完成了对图像进行Sobel算子边缘检测,对图像进 ...

  9. [Python从零到壹] 五十八.图像增强及运算篇之图像锐化Sobel、Laplacian算子实现边缘检测

    欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...

最新文章

  1. 20151020sql2
  2. .NET Core 构建跨平台的桌面应用
  3. Apache ServiceComb Pack 微服务分布式数据最终一致性解决方案
  4. VisualSVNServer的使用
  5. RabbitMq 3.0.1 技术预演资料
  6. CSS Friendly 中的DetailsView的问题
  7. CFileStatus 使用详解
  8. mysql基础之mariadb集群主从架构半同步复制
  9. 技巧:linux开机自动启动脚本(转)
  10. 你的PCB地线走的对吗?为什么要有主地?
  11. POJ 2240 Arbitrage——spfa判正环||flody
  12. 渗透性测试是一种特殊的信息安全服务
  13. fastapi+tortoise单元测试
  14. 当逛书店成为一种怀旧
  15. Android 实现adb手机投屏
  16. Flutter上传多张图片
  17. 2018中国大学生程序设计竞赛 – 网络选拔赛 1001 Buy and Resell [模拟]
  18. 人类一败涂地做图教程_人类一败涂地地图制作教程 创意工坊自制地图方法
  19. Oracle DUL的工作原理和技术实现
  20. Houdini图文笔记:VEX知识点小结(一)

热门文章

  1. SQL2008使用json.net实现XML与JSON互转
  2. TWRP-recovery中文界面安装方法[转]
  3. 手写vue2的Lazyload
  4. TCP/IP(一):数据链路层
  5. 给页面加速,干掉Dom Level 0 Event
  6. 第六篇:python基础之文件处理
  7. vue-cli项目中.postcssrc.js
  8. http --- 前端的缓存
  9. Vue学习笔记(一)—— 什么时候需要import Vue from 'vue'
  10. java虚拟机06-内存分区/新生代、老年代