龙空技术网

3种python3的canny边缘检测之静态,可调节和自适应

易三一世 223

前言:

目前我们对“opencvcanny”大概比较看重,看官们都需要了解一些“opencvcanny”的相关文章。那么小编同时在网络上网罗了一些关于“opencvcanny””的相关资讯,希望大家能喜欢,看官们一起来了解一下吧!

先看高级版的python3的canny的自适应边缘检测:

car的效果图

内容:

1 canny的边缘检测的介绍。

2 三种方法的canny的边缘检测,由浅入深地介绍:固定值的静态,可自调节的,自适应的。

说明:

1 环境:python3.8、opencv4.5.3和matplotlib3.4.3。

2 图片:来自今日头条正版免费图库。

3 实现自适应阈值的canny边缘检测的参考代码和文章:

#基于python2实现自适应阈值的canny本文基于python3,复现一种自适应的阈值分割方法。

上述的代码,本机均有报错,故对代码进行修改,注释和运行。

初级canny:

1 介绍:opencv中给出了canny边缘检测的接口,直接调用:

ret = cv2.canny(img,t1,t2)

即可得到边缘检测的结果ret,其中,t1,t2是需要人为设置的阈值。

2 python的opencv的一行代码即可实现边缘检测。

3 Canny函数及使用:

函数:Canny edges = cv2.Canny(image, threshold1, threashold2)参数:image : 原始图像threshold1 : 阈值1 (minVal)threshold2 : 阈值2 (maxVal)返回值:edges : 边缘图像

4 Canny边缘检测流程:

去噪 --> 梯度 --> 非极大值抑制 --> 滞后阈值

Canny边缘检测算法其实非常复杂,包括4个步骤:1 去噪:用高斯滤波器对图像进行去噪2 梯度:计算梯度3 NMS:在边缘上使用非极大值抑制(NMS)4 滞后阈值:在检测到的边缘上使用双阈值去除假阳性分析所有的边缘及其之间的连接,以保留真正的边缘去除不明显的边缘

5 代码:

import cv2img = cv2.imread("/home/xgj/Desktop/edge_detection/snake.jpeg", cv2.IMREAD_GRAYSCALE)cv2.imshow("snake", img)ret1 = cv2.Canny(img, 100, 200)  #人工设置固定值cv2.imshow("result1", ret1)ret2 = cv2.Canny(img, 20, 60)    #人工设置固定值cv2.imshow("result2", ret2)cv2.waitKey()cv2.destroyAllWindows()

6 操作和过程:

7 原图:

8 疑问:

ret = cv2.canny(img,t1,t2),其中,t1,t2是需要人为设置的阈值,一般人怎么知道具体数值是多少,才是最佳的呀?所以,这是它的缺点。

中级canny:

1 中级canny,就是可调节的阈值,找到最佳的canny边缘检测效果。

2 采用cv2.createTrackbar来调节阈值。

3 代码:

import cv2import numpy as npimg= cv2.imread('/home/xgj/Desktop/edge_detection/3_self_canny/girl.jpeg')cv2.namedWindow('Canny edge detect') #设置窗口,cv2.WINDOW_NORMAL表示窗口大小可自动调节cv2.namedWindow('Original Image') def nothing(x):  pass# 创建两个滑动条,分别控制minVal(最小阈值)、maxVal(最大阈值).# minVal:滑动条名称; 'Canny edge detect':窗口名; 60:滑动条默认滑动位置; 300:最大值 ; nothing:回调函数cv2.createTrackbar('minVal','Canny edge detect',60,300,nothing)cv2.createTrackbar('maxVal','Canny edge detect',100,400,nothing)while(1):  #获得滑动条所在的位置  #cv2.getTrackbarPos(滑动条名称,窗口名);  minVal = cv2.getTrackbarPos('minVal','Canny edge detect')  maxVal = cv2.getTrackbarPos('maxVal','Canny edge detect')  #Canny边缘检测  #cv2.Canny函数参数解析:  # img:原图像名  # minVal:最小梯度  # maxVal:最大梯度  # 5 :5*5大小的高斯滤波器(卷积核),用来消除噪声影响  # L2gradient :求图像梯度,从而进行去除非边界上的点(非极大值抑制)  edgeImage = cv2.Canny(img,minVal,maxVal,5,L2gradient=True)  #显示图片  cv2.imshow('Original Image',img) #原图  cv2.imshow('Canny edge detect',edgeImage) # Canny检测后的图   k = cv2.waitKey(1)  if k ==ord('q')& 0xFF: # 按 q 退出    breakcv2.destroyAllWindows()#销毁窗口

4 操作和效果:

5 原图:

高级canny:

1 自适应canny的算法:

ret = cv2.canny(img,t1,t2)

即算法在运行过程中能够自适应地找到较佳的分割阈值t1,t2。

2 文件结构:

3 main.py代码:

# 主程序:main.py# 第1步:模块导入import numpy as npimport cv2, time, mathfrom matplotlib import pyplot as pltfrom scipy.signal import convolve2d as conv2#两个自定义模块(库)导入from bilateralfilt import bilatfiltfrom dog import deroGauss# 第2步:函数定义# 2-1 获取边缘函数:用高斯滤波器对图像进行去噪def get_edges(I,sd):	dim = I.shape	Idog2d = np.zeros((nang,dim[0],dim[1]))	for i in range(nang):		dog2d = deroGauss(5,sd,angles[i])		Idog2dtemp = abs(conv2(I,dog2d,mode='same',boundary='fill'))		Idog2dtemp[Idog2dtemp<0]=0		Idog2d[i,:,:] = Idog2dtemp	return Idog2d# 2-2 计算梯度def calc_sigt(I,threshval):	M,N = I.shape	ulim = np.uint8(np.max(I))		N1 = np.count_nonzero(I>threshval)	N2 = np.count_nonzero(I<=threshval)	w1 = np.float64(N1)/(M*N)	w2 = np.float64(N2)/(M*N)	try:		u1 = sum(i*np.count_nonzero(np.multiply(I>i-0.5,I<=i+0.5))/N1 for i in range(threshval+1,ulim))		u2 = sum(i*np.count_nonzero(np.multiply(I>i-0.5,I<=i+0.5))/N2 for i in range(threshval+1))				uT = u1*w1+u2*w2		sigt = w1*w2*(u1-u2)**2	except:		return 0	return sigt# 2-3 非极大值抑制(NMS)def nonmaxsup(I,gradang):	dim = I.shape	Inms = np.zeros(dim)	xshift = int(np.round(math.cos(gradang*np.pi/180)))	yshift = int(np.round(math.sin(gradang*np.pi/180)))	Ipad = np.pad(I,(1,),'constant',constant_values = (0,0))	for r in range(1,dim[0]+1):		for c in range(1,dim[1]+1):			maggrad = [Ipad[r-xshift,c-yshift],Ipad[r,c],Ipad[r+xshift,c+yshift]]			if Ipad[r,c] == np.max(maggrad):				Inms[r-1,c-1] = Ipad[r,c]	return Inms# 2-4-1 阈值def threshold(I,uth):	lth = uth/2.5	Ith = np.zeros(I.shape)	Ith[I>=uth] = 255	Ith[I<lth] = 0	Ith[np.multiply(I>=lth, I<uth)] = 100	return Ith# 2-4-2 hysteresis=滞后(效应)def hysteresis(I):	r,c = I.shape	Ipad = np.pad(I,(1,),'edge')	c255 = np.count_nonzero(Ipad==255)	imgchange = True	for i in range(1,r+1):		for j in range(1,c+1):			if Ipad[i,j] == 100:				if np.count_nonzero(Ipad[r-1:r+1,c-1:c+1]==255)>0:					Ipad[i,j] = 255				else:					Ipad[i,j] = 0	Ih = Ipad[1:r+1,1:c+1]	return Ih# 2-4-3 获取最佳阈值def get_threshold(I):	max_sigt = 0	opt_t = 0	ulim = np.uint8(np.max(I))	print(ulim,'\n')	for t in range(ulim+1):		sigt = calc_sigt(I,t)		if sigt > max_sigt:			max_sigt = sigt			opt_t = t	print ('optimal high threshold: ',opt_t,'\n')	return opt_t# 第3步:图片读取# 读取原图img = cv2.imread('/home/xgj/Desktop/edge_detection/3_self_canny/car.jpeg')# 判断原图大小,如果大于多少,就调节图片大小# 否则不调节while img.shape[0] > 1100 or img.shape[1] > 1100:    img = cv2.resize(img,None, fx=0.5,fy=0.5,interpolation = cv2.INTER_AREA)# 转换为gray灰度图gimg = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)dim = img.shape #获取图片大小# 第4步:开始图像的canny的自适应操作#Bilateral filtering=双边滤波print("总共有2步:有一定的耗时,与图片大小有关\n")print("第1步:\n")print ('Bilateral filtering...\n')# 双边滤波:达到保边去噪gimg = bilatfilt(gimg,5,3,10)print ('after bilat: ',np.max(gimg),'\n')#获取时间计时stime = time.time()angles = [0,45,90,135]nang = len(angles)#Gradient of Image=图片的梯度print ('Calculating Gradient...\n')img_edges = get_edges(gimg,2)print ('after gradient: ',np.max(img_edges),'\n')#Non-max suppression:在边缘上使用非极大值抑制(NMS)print ('Suppressing Non-maximas...\n')for n in range(nang):	img_edges[n,:,:] = nonmaxsup(img_edges[n,:,:],angles[n])print ('after nms: ', np.max(img_edges),'\n')print ('请关掉matplotlib的图形窗口,进行下一步自适应。。。\n')img_edge = np.max(img_edges,axis=0)lim = np.uint8(np.max(img_edge))plt.imshow(img_edge)plt.show()print("第2步 函数调用:\n")# 计算阈值print ('Calculating Threshold...\n')th = get_threshold(gimg)the = get_threshold(img_edge)# 获取阈值print ('\nThresholding...\n')img_edge = threshold(img_edge, the*0.25)# 在边缘上使用非极大值抑制,滞后效应,获取自适应阈值print ('Applying Hysteresis...\n')img_edge = nonmaxsup(hysteresis(img_edge),90)# 第5步:canny调用获取的自适应阈值# 获取自适应的阈值采用canny进行边缘检测img_canny = cv2.Canny(np.uint8(gimg),th/3,th)cv2.imshow('Uncanny',img_edge)cv2.imshow('Canny',img_canny)print( 'Time taken :: ', str(time.time()-stime)+' seconds...\n')print("结束!!")cv2.waitKey(0)

4 dog.py代码:

import numpy as npimport mathdef deroGauss(w=5,s=1,angle=0):	wlim = (w-1)/2	y,x = np.meshgrid(np.arange(-wlim,wlim+1),np.arange(-wlim,wlim+1))	G = np.exp(-np.sum((np.square(x),np.square(y)),axis=0)/(2*np.float64(s)**2))	G = G/np.sum(G)	dGdx = -np.multiply(x,G)/np.float64(s)**2	dGdy = -np.multiply(y,G)/np.float64(s)**2	angle = angle*math.pi/180	dog = math.cos(angle)*dGdx + math.sin(angle)*dGdy	return dog

5 bilateralfilt.py代码:

import numpy as np# 双边滤波def bilatfilt(I,w,sd,sr):	dim = I.shape	Iout= np.zeros(dim)	wlim = (w-1)//2	y,x = np.meshgrid(np.arange(-wlim,wlim+1),np.arange(-wlim,wlim+1))	g = np.exp(-np.sum((np.square(x),np.square(y)),axis=0)/(2*(np.float64(sd)**2)))	Ipad = np.pad(I,(wlim,),'edge')	for r in range(wlim,dim[0]+wlim):		for c in range(wlim,dim[1]+wlim):			Ix = Ipad[r-wlim:r+wlim+1,c-wlim:c+wlim+1]			s = np.exp(-np.square(Ix-Ipad[r,c])/(2*(np.float64(sr)**2)))			k = np.multiply(g,s)			Iout[r-wlim,c-wlim] = np.sum(np.multiply(k,Ix))/np.sum(k)				return Iout

6 原图:

7 效果图:本文第一个gif图,此处省略。

小结:

1 本文由浅入深,总结的很好,适合收藏。

2 对于理解python的opencv的canny的边缘检测,很有帮助。

3 本文高级版canny自适应的算法参考2篇文章,虽然我进行代码的删除,注释,修改,优化等操作,故我不标注原创,对原作者表达敬意。

4 自己总结和整理,分享出来,希望对大家有帮助。

标签: #opencvcanny