百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

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

haoteby 2024-12-23 10:28 3 浏览

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

内容:

1 canny的边缘检测的介绍。

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

说明:

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

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

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

#基于python2实现自适应阈值的canny
https://github.com/sadimanna/canny

#本文基于python3,复现一种自适应的阈值分割方法。
https://blog.csdn.net/lyxleft/article/details/91558726?spm=1001.2014.3001.5501

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

初级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 cv2

img = 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 cv2
import numpy as np

img= 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 退出
    break
cv2.destroyAllWindows()#销毁窗口

4 操作和效果:

5 原图:

高级canny:

1 自适应canny的算法:

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

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

2 文件结构:

3 main.py代码:


# 主程序:main.py

# 第1步:模块导入
import numpy as np
import cv2, time, math
from matplotlib import pyplot as plt
from scipy.signal import convolve2d as conv2
#两个自定义模块(库)导入
from bilateralfilt import bilatfilt
from 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 np
import math

def 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 自己总结和整理,分享出来,希望对大家有帮助。

相关推荐

单点登录(SSO)解决方案介绍(单点登录概念)

一、单点登录的介绍单点登录(SingleSignOn),简称为SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系...

系统登录的三种方式,哪一种更安全?

登录是一个高频的动作,笔者抓住这一个小点,分析了系统登录的几种方式和对应的场景。今天谈谈登录。登录即用户输入用户名和密码登录进系统中。B端系统,对于登录的业务场景有两种(可能不止,目前遇到过这两种):...

到底什么是单点登录(SSO)?(什么叫做单点登录)

什么是单点登录?单点登录(SingleSign-On,简称SSO)是一种集中式的身份验证和授权机制,用户只需在一处输入一次凭证(例如用户名和密码)就可以访问多个相关但独立的软件系统。在数字化时代,...

5年稳如老狗的单点登录系统,到底是怎么搞出来的?

说到单点登录(SingleSign-On,简称SSO),大家的第一反应可能是——啊不就是登录一次,能到处串门儿嘛?别说,还真差不多,就是这么个意思。但真要搭一套好用、耐造、还能扛住公司里各种奇奇怪...

这些负载均衡都解决哪些问题?服务、网关、NGINX?

在微服务项目中,有服务的负载均衡、网关的负载均衡、Nginx的负载均衡,这几个负载均衡分别用来解决什么问题呢?一、服务的负载均衡先抛出一个问题:...

Nginx负载均衡最全详解(4大算法原理机制)

Nginx在大型网站架构很重要,也是大厂重点考察方向,今天我就重点来详解Nginx负载均衡@mikechen本篇已收于mikechen原创超30万字《阿里架构师进阶专题合集》里面。Nginx负载均衡N...

负载均衡 Nginx Session 一致性(nginx 负载均衡 会话保持)

HTTPS请求跳转...

监控Oracle Cloud负载均衡器:Applications Manager释放最佳性能

设想你正在运营一个受欢迎的在线学习平台,在考试前的高峰期,平台流量激增。全球的学生同时登录,观看视频、提交作业和参加测试。如果OracleCloud负载均衡器不能高效地分配流量,或者后端服务器难...

Nginx负载均衡:nginx.conf配置文件说明!

大家好,欢迎来到程序视点!我是你们的老朋友.小二!在此记录下Nginx服务器nginx.conf负载均衡的配置文件说明,部分注释收集与网络.关于nginx.conf基本的配置,请查看上一篇文章!Ng...

Java高可用系统架构中的负载均衡策略

Java高可用系统架构中的负载均衡策略在现代的分布式系统中,负载均衡策略是构建高可用系统的基石。Java开发者需要深刻理解这些策略,以便打造稳定且高效的系统。接下来,让我们一起揭开负载均衡的神秘面纱。...

深入对比Nginx、LVS和HAProxy,选择最合适负载均衡方案!

关注...

Spring Boot3 客户端负载均衡全解析:从原理到实战

在当今互联网大厂后端技术开发的激烈竞争环境中,构建高效、稳定的微服务架构是核心诉求。其中,SpringBoot3作为热门开发框架,其客户端负载均衡功能对于提升系统性能、保障服务稳定性起着关键作用。...

MySql高可用集群MySQL Router负载均衡读写分离

名词解释MGR:MysqlGroupReplication组复制,多台MySQL服务器在同一组中会自动保持同步状态,当某台服务器故障时,整个复制组依然可以保持正常并对外提供服务。...

性能测试之tomcat+nginx负载均衡(nginx tomcat)

nginxtomcat配置准备工作:两个tomcat执行命令cp-rapache-tomcat-8.5.56apache-tomcat-8.5.56_2修改被复制的tomcat2下con...

win10/11双网卡链路聚合叠加负载均衡提升网速解决网卡网速瓶颈!

双网卡链路聚合一种网络配置技术,通过将多个物理网卡绑定在一起,形成一个逻辑上的网络接口,以提高网络的可靠性、可用性和性能。这种技术通常用于服务器和网络设备中,以实现负载均衡、冗余和高可用性。本机环境:...