3种python3的canny边缘检测之静态,可调节和自适应
off999 2024-09-20 22:50 32 浏览 0 评论
△ 先看高级版的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 自己总结和整理,分享出来,希望对大家有帮助。
相关推荐
- 第九章:Python文件操作与输入输出
-
9.1文件的基本操作9.1.1打开文件理论知识:在Python中,使用open()函数来打开文件。open()函数接受两个主要参数:文件名和打开模式。打开模式决定了文件如何被使用,常见的模式有:&...
- Python的文件处理
-
一、文件处理的流程1.打开文件,得到文件句柄并赋值给一个变量2.通过句柄对文件进行操作3.关闭文件示例:d=open('abc')data1=d.read()pri...
- Python处理文本的25个经典操作
-
Python处理文本的优势主要体现在其简洁性、功能强大和灵活性。具体来说,Python提供了丰富的库和工具,使得对文件的读写、处理变得轻而易举。简洁的文件操作接口Python通过内置的open()函数...
- Python学不会来打我(84)python复制文件操作总结
-
上一篇文章我们分享了python读写文件的操作,主要用到了open()、read()、write()等方法。这一次是在文件读写的基础之上,我们分享文件的复制。#python##python自学##...
- python 文件操作
-
1.检查目录/文件使用exists()方法来检查是否存在特定路径。如果存在,返回True;如果不存在,则返回False。此功能在os和pathlib模块中均可用,各自的用法如下。#os模块中e...
- 《文件操作(读写文件)》
-
一、文件操作基础1.open()函数核心语法file=open("filename.txt",mode="r",encoding="utf-8"...
- 栋察宇宙(二十一):Python 文件操作全解析
-
分享乐趣,传播快乐,增长见识,留下美好。亲爱的您,这里是LearingYard学苑!今天小编为大家带来“Python文件操作全解析”欢迎您的访问!Sharethefun,spreadthe...
- 值得学习练手的70个Python项目(附代码),太实用了
-
Python丰富的开发生态是它的一大优势,各种第三方库、框架和代码,都是前人造好的“轮子”,能够完成很多操作,让你的开发事半功倍。下面就给大家介绍70个通过Python构建的项目,以此来学习Pytho...
- python图形化编程:猜数字的游戏
-
importrandomnum=random.randint(1,500)running=Truetimes=0##总的次数fromtkinterimport*##导入所有tki...
- 一文讲清Python Flask的Web编程知识
-
刚入坑Python做Web开发的新手,还在被配置臃肿、启动繁琐折磨?Flask这轻量级框架最近又火出圈,凭5行代码启动Web服务的极致简洁,让90后程序员小张直呼真香——毕竟他刚用这招把部署时间从半小...
- 用python 编写一个hello,world
-
第一种:交互式运行一个hello,world程序:这是写python的第一步,也是学习各类语言的第一步,就是用这种语言写一个hello,world程序.第一步,打开命令行窗口,输入python,第二步...
- python编程:如何使用python代码绘制出哪些常见的机器学习图像?
-
专栏推荐绘图的变量单变量查看单变量最方便的无疑是displot()函数,默认绘制一个直方图,并你核密度估计(KDE)sns.set(color_codes=True)np.random.seed(su...
- 如何编写快速且更惯用的 Python 代码
-
Python因其可读性而受到称赞。这使它成为一种很好的第一语言,也是脚本和原型设计的流行选择。在这篇文章中,我们将研究一些可以使您的Python代码更具可读性和惯用性的技术。我不仅仅是pyt...
- Python函数式编程的详细分析(代码示例)
-
本篇文章给大家带来的内容是关于Python函数式编程的详细分析(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。FunctionalProgramming,函数式编程。Py...
- 编程小白学做题:Python 的经典编程题及详解,附代码和注释(七)
-
适合Python3+的6道编程练习题(附详解)1.检查字符串是否以指定子串开头题目描述:判断字符串是否以给定子串开头(如"helloworld"以"hello&...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python读取文件夹下所有文件 (59)
- java调用python脚本 (56)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)