python+opencv实现canny边缘检测


【步骤】

使用高斯滤波器滤波,平滑和滤去噪声

计算梯度强度和方向(本文中使用Sobel算子实现)

应用非极大抑制

双阈值滤波

抑制孤立的弱边缘


一、实现代码

1.引入库

import cv2
import numpy as np 

2. 返回sobel边缘检测的结果

def sobel(image):
	"""返回sobel边缘检测的结果"""
	x = cv2.Sobel(image, cv2.CV_16S, 1, 0)
	y = cv2.Sobel(image, cv2.CV_16S, 0, 1)

	absX = cv2.convertScaleAbs(x)
	absY = cv2.convertScaleAbs(y)

	image = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
	grad = np.arctan2(absY, absX)

	return image, grad
def suppression(img, D):
    """ Non-maximum suppression
    只有是极大值的时候,才赋值,否则为0
    Args:
        img: Sobel算子检测后的图像
        D: 梯度值
    Returns:
        image
    """

    M, N = img.shape
    res = np.zeros((M,N), dtype=np.int32)

    for i in range(M):
        for j in range(N):
            # find neighbour pixels to visit from the gradient directions
            where = round_angle(D[i, j])
            try:
                if where == 0:
                    if (img[i, j] >= img[i, j - 1]) and (img[i, j] >= img[i, j + 1]):
                        res[i,j] = img[i,j]
                elif where == 90:
                    if (img[i, j] >= img[i - 1, j]) and (img[i, j] >= img[i + 1, j]):
                        res[i,j] = img[i,j]
                elif where == 135:
                    if (img[i, j] >= img[i - 1, j - 1]) and (img[i, j] >= img[i + 1, j + 1]):
                        res[i,j] = img[i,j]
                elif where == 45:
                    if (img[i, j] >= img[i - 1, j + 1]) and (img[i, j] >= img[i + 1, j - 1]):
                        res[i,j] = img[i,j]
            except IndexError as e:
                """ Todo: Deal with pixels at the image boundaries. """
                pass
    return res

3. 返回梯度方向的角度

def round_angle(angle):
    """ Input angle must be in [0,180) 
    返回梯度方向的角度
    """

    angle = np.rad2deg(angle) % 180 #弧度转换成角度
    if (0 <= angle < 22.5) or (157.5 <= angle < 180):
        angle = 0
    elif (22.5 <= angle < 67.5):
        angle = 45
    elif (67.5 <= angle < 112.5):
        angle = 90
    elif (112.5 <= angle < 157.5):
        angle = 135
    return angle

4. 双阈值提取强边缘和弱边缘

def threshold(img, t, T):
    """ Thresholding
    Iterates through image pixels and marks them as WEAK and STRONG edge
    pixels based on the threshold values.
    双阈值提取强边缘和弱边缘
    Args:
        img: Numpy ndarray of image to be processed (suppressed image)
        t: lower threshold
        T: upper threshold
    Return:
        img: Thresholdes image
    """
    # define gray value of a WEAK and a STRONG pixel
    cf = {
        'WEAK': np.int32(50),
        'STRONG': np.int32(255),
    }

    # get strong pixel indices
    strong_i, strong_j = np.where(img > T)

    # get weak pixel indices
    weak_i, weak_j = np.where((img >= t) & (img <= T))

    # get pixel indices set to be zero
    zero_i, zero_j = np.where(img < t)

    # set values
    img[strong_i, strong_j] = cf.get('STRONG')
    img[weak_i, weak_j] = cf.get('WEAK')
    img[zero_i, zero_j] = np.int32(0)

    return (img, cf.get('WEAK'))
def tracking(img, weak, strong=255):
    M, N = img.shape
    for i in range(M):
        for j in range(N):
            if img[i, j] == weak:
                # check if one of the neighbours is strong (=255 by default)
                try:
                    if ((img[i + 1, j] == strong) or (img[i - 1, j] == strong)
                         or (img[i, j + 1] == strong) or (img[i, j - 1] == strong)
                         or (img[i+1, j + 1] == strong) or (img[i-1, j - 1] == strong)
                         or (img[i-1, j + 1] == strong) or (img[i-1, j + 1] == strong)):
                        img[i, j] = strong
                    else:
                        img[i, j] = 0
                except IndexError as e:
                    pass
    return 

二、完整程序

import cv2
import numpy as np 

# def NMS():

def sobel(image):
	"""返回sobel边缘检测的结果"""
	x = cv2.Sobel(image, cv2.CV_16S, 1, 0)
	y = cv2.Sobel(image, cv2.CV_16S, 0, 1)

	absX = cv2.convertScaleAbs(x)
	absY = cv2.convertScaleAbs(y)

	image = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
	grad = np.arctan2(absY, absX)

	return image, grad

def suppression(img, D):
    """ Non-maximum suppression
    只有是极大值的时候,才赋值,否则为0
    Args:
        img: Sobel算子检测后的图像
        D: 梯度值
    Returns:
        image
    """

    M, N = img.shape
    res = np.zeros((M,N), dtype=np.int32)

    for i in range(M):
        for j in range(N):
            # find neighbour pixels to visit from the gradient directions
            where = round_angle(D[i, j])
            try:
                if where == 0:
                    if (img[i, j] >= img[i, j - 1]) and (img[i, j] >= img[i, j + 1]):
                        res[i,j] = img[i,j]
                elif where == 90:
                    if (img[i, j] >= img[i - 1, j]) and (img[i, j] >= img[i + 1, j]):
                        res[i,j] = img[i,j]
                elif where == 135:
                    if (img[i, j] >= img[i - 1, j - 1]) and (img[i, j] >= img[i + 1, j + 1]):
                        res[i,j] = img[i,j]
                elif where == 45:
                    if (img[i, j] >= img[i - 1, j + 1]) and (img[i, j] >= img[i + 1, j - 1]):
                        res[i,j] = img[i,j]
            except IndexError as e:
                """ Todo: Deal with pixels at the image boundaries. """
                pass
    return res

def round_angle(angle):
    """ Input angle must be in [0,180) 
    返回梯度方向的角度
    """

    angle = np.rad2deg(angle) % 180 #弧度转换成角度
    if (0 <= angle < 22.5) or (157.5 <= angle < 180):
        angle = 0
    elif (22.5 <= angle < 67.5):
        angle = 45
    elif (67.5 <= angle < 112.5):
        angle = 90
    elif (112.5 <= angle < 157.5):
        angle = 135
    return angle

def threshold(img, t, T):
    """ Thresholding
    Iterates through image pixels and marks them as WEAK and STRONG edge
    pixels based on the threshold values.
    双阈值提取强边缘和弱边缘
    Args:
        img: Numpy ndarray of image to be processed (suppressed image)
        t: lower threshold
        T: upper threshold
    Return:
        img: Thresholdes image
    """
    # define gray value of a WEAK and a STRONG pixel
    cf = {
        'WEAK': np.int32(50),
        'STRONG': np.int32(255),
    }

    # get strong pixel indices
    strong_i, strong_j = np.where(img > T)

    # get weak pixel indices
    weak_i, weak_j = np.where((img >= t) & (img <= T))

    # get pixel indices set to be zero
    zero_i, zero_j = np.where(img < t)

    # set values
    img[strong_i, strong_j] = cf.get('STRONG')
    img[weak_i, weak_j] = cf.get('WEAK')
    img[zero_i, zero_j] = np.int32(0)

    return (img, cf.get('WEAK'))

def tracking(img, weak, strong=255):
    M, N = img.shape
    for i in range(M):
        for j in range(N):
            if img[i, j] == weak:
                # check if one of the neighbours is strong (=255 by default)
                try:
                    if ((img[i + 1, j] == strong) or (img[i - 1, j] == strong)
                         or (img[i, j + 1] == strong) or (img[i, j - 1] == strong)
                         or (img[i+1, j + 1] == strong) or (img[i-1, j - 1] == strong)
                         or (img[i-1, j + 1] == strong) or (img[i-1, j + 1] == strong)):
                        img[i, j] = strong
                    else:
                        img[i, j] = 0
                except IndexError as e:
                    pass
    return img


if __name__ == '__main__':
	I = cv2.imread('./1.jpg')
	I = cv2.resize(I, (300,400))
	I = cv2.cvtColor(I, cv2.COLOR_RGB2GRAY)

	sobelI, gradI = sobel(I) # 返回sobel结果和梯度方向

	imageNMS = suppression(sobelI, gradI) # 返回非极大抑制后的结果

	imageThres, weak = threshold(imageNMS,40,80) # 返回阈值后的结果

	imageRes = tracking(imageThres, weak, strong=255) # 抑制孤立的边缘

	cv2.imwrite('./soble.jpg', sobelI)
	cv2.imwrite('./imageNMS.jpg', imageNMS)
	cv2.imwrite('./imageThres.jpg', imageThres)
	cv2.imwrite('./imageRes.jpg', imageRes)

三、边缘检测效果

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

极客程序设计

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值