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

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

off999 2024-09-20 22:50 27 浏览 0 评论

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

内容:

1 canny的边缘检测的介绍。

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

说明:

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

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

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

Bash
#基于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边缘检测的接口,直接调用:

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

相关推荐

Python中的两个内置函数id()和type()

id()>>>id(3)2531362761072>>>id(3.222222)2531397393680>>>id(3.0)25313...

python 函数中,如何将另一个函数作为参数传递

python函数中,如何将另一个函数作为参数传递,类似C#委托defadd(a,b):"""这是一个简单的加法函数,接受两个参数并返回它们的和。""...

Python性能暴涨10倍的终极指南:7个核心技巧+代码压缩秘籍

提升Python程序运行性能,使代码运行更流畅更快,以及压缩代码,减小代码大小,下面的方法仅供大家参考,有什么更好的方法在评论区说说。1.使用NumPy/SciPy替代纯Python循环...

Python 匿名函数(Lambda 函数)详解

匿名函数(AnonymousFunction),在Python中称为lambda函数,是一种不需要使用def关键字定义的小型函数。它主要用于简化代码,特别适合需要函数对象的地方。1.基...

Python学习笔记 | 匿名函数lambda、映射函数map和过滤函数filter

什么是匿名函数?定义:没有函数名的自定义函数场景:函数体非常简单,使用次数很少,没有必要声明函数,通常搭配高阶函数使用。高阶函数是能够把函数当成参数进行传递的函数,如:映射函数map和过滤函数fil...

python练习:自定义函数调用:商品购物实例

1、商品录入dict_myshanpin_iof={101:{"商品名称":"毛毛熊","单价":25},102:{"商品名称":...

Python中如何使用Lambda函数(lambda在python中的用法)

Python和其他编程语言一样,都在其语法中添加了lambda函数,Pythonlambda是匿名函数,比常规Python自定义函数有更简洁的语法。虽然Lambda语法在开始时可能会觉得有点混乱,...

8-Python内置函数(python内置函数代码)

Python提供了丰富的内置函数,这些函数可以直接使用而无需导入任何模块。以下是一些常用的内置函数及其示例:1-print()1-1-说明输出指定的信息到控制台。1-2-例子2-len()2-1-说...

用Python进行函数式编程(python函数程序)

什么是函数式编程?函数式程序设计是一种编程范例,它把计算当作数学函数的评价,避免状态和可变数据。换句话说,函数编程(FunctionalProgramming,FP)促进没有副作用和不变变量的代码。它...

python 函数进阶(python如何进阶)

1.有名函数和匿名函数#该函数有名称,名称是adddefadd(x,y):returnx+y#改函数没有名称属于匿名函数,也叫lambda表达式lambda_add...

python自学者的分享:自定义函数、参数作用域、匿名函数、装饰器

#自定义新函数函数名newhsdefnewhs(a,b=1):#b的默认值为1,在没有传入b值时,采用默认值,,默认值参数不能放前边returna-bprint(newh...

Python 函数式编程的 8 大核心技巧,不允许你还不会

函数式编程是一种强调使用纯函数、避免共享状态和可变数据的编程范式。Python虽然不是纯函数式语言,但提供了丰富的函数式编程特性。以下是Python函数式编程的8个核心技巧:1.纯函数(...

零基础到发布:手把手教你创建并分发 Python 自定义库

作为程序员,我们经常依赖各种外部库来解决不同的问题。这些库由技术娴熟的开发者创建,为我们提供了节省时间和精力的解决方案。但你是否曾想过:“我也能创建属于自己的自定义库吗?”答案是肯定的!本文将为你详细...

打工人学Python:(七)自定义函数,打造自己的武器库

从一个简单的函数开始#!/usr/bin/envpython#-*-encoding:utf-8-*-'''@Purpose:Wordcount@...

肖sir_python自定义函数format、zip函数

python自定义函数一、常见的自定义函数已经学过的函数:list、print、set、str、type、tuple、dict、range、input等今天学的函数:format二、实战讲解(一)f...

取消回复欢迎 发表评论: