R329-canny边缘检测

本文介绍了如何在Jupyter Notebook中使用Python和OpenCV库,通过Canny算法实现图像边缘检测,包括了从摄像头捕获、图像预处理到Canny边缘检测的完整流程。详细讲解了Canny算法的工作原理,并提供了详细的代码示例。
摘要由CSDN通过智能技术生成

今天的简单,利用canny算法实现边缘检测,再opencv内已经内置,只需一行代码即可实现。具体代码如下:
代码运行环境为jupyter notebook

#-*- coding: utf-8 -*-
import time
import cv2

cap = cv2.VideoCapture(0)
#设置图像的高
cap.set(3,240)
#设置图像的宽,这里虽然设置的是240但是实际上是320,
cap.set(4,240)
#写入指定设备
f = open('/dev/fb0','wb')

while True:
    st = time.time()
    ret,img = cap.read()
    if ret:
        #截取240*240的图像,lcd最大能显示的是240*240个16位像素
        img = img[:,0:240]
        #将捕获的一帧图像灰度化处理
        img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
        #将灰度图顺时针旋转180度
        img = cv2.rotate(img,cv2.ROTATE_180)
        #canny边缘检测
        img = cv2.Canny(img, 200, 300)
        #转换为16位色彩,(因为lcd是16位显示)
        img = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR565)
        cv2.putText(img, "{0}" .format(str(1 / (time.time() - st))), (0, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 255), 1)
        #写入图像的二进制数据
        f.seek(0)
        f.write(bytearray(img))
 

canny边缘检测算法非常复杂,但是也很有趣,他有五个步骤,即使用高斯滤波器对图像进行去噪,计算梯度,在边缘上使用非最大抑制(NMS),在检测到边缘上使用双阈值去除假阳性,最后还会分析所有边缘及其之间的连接,以保留真正的边缘并不消除不明显的边缘。我这里给出张平老师写的《opencv算法 基于python和c++》精讲里的代码
main.py

 # -*- coding: utf-8 -*-
 import numpy as np
 import sys
 import math
 import cv2
 import sobel #注意sobel边缘检测
 #边缘检测
 #非极大值抑制
 def non_maximum_suppression_default(dx,dy):
     #边缘强度
     edgeMag = np.sqrt(np.power(dx,2.0) + np.power(dy,2.0))
     #宽、高
     rows,cols = dx.shape
     #梯度方向
     gradientDirection = np.zeros(dx.shape)
     #边缘强度非极大值抑制
     edgeMag_nonMaxSup = np.zeros(dx.shape)
     for r in range(1,rows-1):
         for c in range(1,cols-1):
             #angle 的范围 [0,180] [-180,0]
             angle = math.atan2(dy[r][c],dx[r][c])/math.pi*180
             gradientDirection[r][c] = angle
             #左 / 右方向
             if(abs(angle)<22.5 or abs(angle) >157.5):
                 if(edgeMag[r][c]>edgeMag[r][c-1] and edgeMag[r][c] > edgeMag[r][c+1]):
                     edgeMag_nonMaxSup[r][c] = edgeMag[r][c]
             #左上 / 右下方向  
             if(angle>=22.5 and angle < 67.5 or(-angle > 112.5 and -angle <= 157.5)):
                 if(edgeMag[r][c] > edgeMag[r-1][c-1] and edgeMag[r][c]>edgeMag[r+1][c+1]):
                      edgeMag_nonMaxSup[r][c] = edgeMag[r][c]
             #上 / 下方向
             if((angle>=67.5 and angle<=112.5) or (angle>=-112.5 and angle<=-67.5)):
                 if(edgeMag[r][c] > edgeMag[r-1][c] and edgeMag[r][c] > edgeMag[r+1][c]):
                     edgeMag_nonMaxSup[r][c] = edgeMag[r][c]
             #右上 / 左下方向
             if((angle>112.5 and angle<=157.5) or(-angle>=22.5 and -angle< 67.5 )):
                 if(edgeMag[r][c]>edgeMag[r-1][c+1] and edgeMag[r][c] > edgeMag[r+1][c-1]):
                     edgeMag_nonMaxSup[r][c] = edgeMag[r][c]
     return edgeMag_nonMaxSup
 #非极大值抑制:插值比较
 def non_maximum_suppression_Inter(dx,dy):
     #边缘强度
     edgeMag = np.sqrt(np.power(dx,2.0)+np.power(dy,2.0))
     #宽、高
     rows,cols = dx.shape
     #梯度方向
     gradientDirection = np.zeros(dx.shape)
     #边缘强度的非极大值抑制
     edgeMag_nonMaxSup = np.zeros(dx.shape)
     for r in range(1,rows-1):
         for c in range(1,cols-1):
             if dy[r][c] ==0 and dx[r][c] == 0:
                 continue
             #angle的范围 [0,180],[-180,0]
             angle = math.atan2(dy[r][c],dx[r][c])/math.pi*180
             gradientDirection[r][c] = angle
             #左上方和上方的插值 右下方和下方的插值
             if (angle > 45 and angle <=90) or (angle > -135 and angle <=-90):
                 ratio = dx[r][c]/dy[r][c]
                 leftTop_top = ratio*edgeMag[r-1][c-1]+(1-ratio)*edgeMag[r-1][c]
                 rightBottom_bottom = (1-ratio)*edgeMag[r+1][c] + ratio*edgeMag[r+1][c+1]
                 if edgeMag[r][c] >  leftTop_top and edgeMag[r][c] > rightBottom_bottom:
                     edgeMag_nonMaxSup[r][c]  = edgeMag[r][c]
             #右上方和上方的插值 左下方和下方的插值
             if (angle>90 and angle<=135) or (angle>-90 and angle <= -45):
                 ratio = abs(dx[r][c]/dy[r][c])
                 rightTop_top = ratio*edgeMag[r-1][c+1] + (1-ratio)*edgeMag[r-1][c]
                 leftBottom_bottom = ratio*edgeMag[r+1][c-1] + (1-ratio)*edgeMag[r+1][c]
                 if edgeMag[r][c] > rightTop_top and edgeMag[r][c] > leftBottom_bottom:
                     edgeMag_nonMaxSup[r][c]  = edgeMag[r][c]
             #左上方和左方的插值 右下方和右方的插值
             if (angle>=0 and angle <=45) or (angle>-180 and angle <= -135):
                 ratio = dy[r][c]/dx[r][c]
                 rightBottom_right = ratio*edgeMag[r+1][c+1]+(1-ratio)*edgeMag[r][c+1]
                 leftTop_left = ratio*edgeMag[r-1][c-1]+(1-ratio)*edgeMag[r][c-1]
                 if edgeMag[r][c] > rightBottom_right and edgeMag[r][c] > leftTop_left:
                     edgeMag_nonMaxSup[r][c]  = edgeMag[r][c]
             #右上方和右方的插值 左下方和左方的插值
             if(angle>135 and angle<=180) or (angle>-45 and angle <=0):
                 ratio = abs(dy[r][c]/dx[r][c])
                 rightTop_right = ratio*edgeMag[r-1][c+1]+(1-ratio)*edgeMag[r][c+1]
                 leftBottom_left = ratio*edgeMag[r+1][c-1]+(1-ratio)*edgeMag[r][c-1]
                 if edgeMag[r][c] > rightTop_right and edgeMag[r][c] > leftBottom_left:
                     edgeMag_nonMaxSup[r][c]  = edgeMag[r][c]
     return edgeMag_nonMaxSup
 #判断一个点的坐标是否在图像范围内
 def checkInRange(r,c,rows,cols):
     if r>=0 and r<rows and c>=0 and c<cols:
         return True
     else:
         return False
 def trace(edgeMag_nonMaxSup,edge,lowerThresh,r,c,rows,cols):
     #大于阈值为确定边缘点
     if edge[r][c] == 0:
         edge[r][c]=255
         for i in range(-1,2):
             for j in range(-1,2):
                 if checkInRange(r+i,c+j,rows,cols) and edgeMag_nonMaxSup[r+i][c+j] >= lowerThresh:
                     trace(edgeMag_nonMaxSup,edge,lowerThresh,r+i,c+j,rows,cols)
 #滞后阈值
 def hysteresisThreshold(edge_nonMaxSup,lowerThresh,upperThresh):
     #宽高
     rows,cols = edge_nonMaxSup.shape
     edge = np.zeros(edge_nonMaxSup.shape,np.uint8)
     for r in range(1,rows-1):
         for c in range(1,cols-1):
             #大于高阈值,设置为确定边缘点,而且以该点为起始点延长边缘
             if edge_nonMaxSup[r][c] >= upperThresh:
                 trace(edgeMag_nonMaxSup,edge,lowerThresh,r,c,rows,cols)
             #小于低阈值,被剔除
             if edge_nonMaxSup[r][c]< lowerThresh:
                 edge[r][c] = 0
     return edge
 #主函数
 if __name__ =="__main__":
     if len(sys.argv)>1:
         image = cv2.imread(sys.argv[1],cv2.CV_LOAD_IMAGE_GRAYSCALE)
     else:
         print("Usge:python canny.py imageFile")
     # ------- canny 边缘检测 -----------
     #第一步: 基于 sobel 核的卷积
     image_sobel_x,image_sobel_y = sobel.sobel(image,3)
     #边缘强度:两个卷积结果对应位置的平方和
     edge = np.sqrt(np.power(image_sobel_x,2.0) + np.power(image_sobel_y,2.0))
     #边缘强度的灰度级显示
     edge[edge>255] = 255
     edge = edge.astype(np.uint8)
     cv2.imshow("sobel edge",edge)
     #第二步:非极大值抑制
     edgeMag_nonMaxSup = non_maximum_suppression_default(image_sobel_x,image_sobel_y)
     edgeMag_nonMaxSup[edgeMag_nonMaxSup>255] =255
     edgeMag_nonMaxSup = edgeMag_nonMaxSup.astype(np.uint8)
     cv2.imshow("edgeMag_nonMaxSup",edgeMag_nonMaxSup)
     #第三步:双阈值滞后阈值处理,得到 canny 边缘
     #滞后阈值的目的就是最后决定处于高阈值和低阈值之间的是否为边缘点
     edge = hysteresisThreshold(edgeMag_nonMaxSup,60,180)
     lowerThresh = 40
     upperThresh = 150
     cv2.imshow("canny",edge)
     cv2.imwrite("canny.jpg",edge)
     # -------以下是为了单阈值与滞后阈值的结果比较 ------
     #大于高阈值 设置为白色 为确定边缘
     EDGE = 255
     #小于低阈值的设置为黑色 表示不是边缘,被剔除
     NOEDGE = 0
     #而大于等于低阈值 小于高阈值的设置为灰色,标记为可能的边缘
     POSSIBLE_EDGE = 128
     tempEdge = np.copy(edgeMag_nonMaxSup)
     rows,cols = tempEdge.shape
     for r in xrange(rows):
         for c in xrange(cols):
             if tempEdge[r][c]>=upperThresh:
                 tempEdge[r][c] = EDGE
             elif tempEdge[r][c]<lowerThresh:
                 tempEdge[r][c] = NOEDGE
             else:
                 tempEdge[r][c] = POSSIBLE_EDGE
     cv2.imshow("tempEdge",tempEdge)
     lowEdge = np.copy(edgeMag_nonMaxSup)
     lowEdge[lowEdge>60] = 255
     lowEdge[lowEdge<60] = 0
     cv2.imshow("lowEdge",lowEdge)
     upperEdge = np.copy(edgeMag_nonMaxSup)
     upperEdge[upperEdge>180]=255
     upperEdge[upperEdge<=180]=0
     cv2.imshow("upperEdge",upperEdge)
     cv2.waitKey(0)
     cv2.destroyAllWindows()
         

sobel.py

# -*- coding: utf-8 -*-
import numpy as np
import sys
import math
import cv2
from scipy import signal
#二项式展开式的系数,即平滑系数
def pascalSmooth(n):
    pascalSmooth = np.zeros([1,n],np.float32)
    for i in range(n):
        pascalSmooth[0][i] = math.factorial(n -1)/(math.factorial(i)*math.factorial(n-1-i))
    return pascalSmooth
#计算差分
def pascalDiff(n):
    pascalDiff = np.zeros([1,n],np.float32)
    pascalSmooth_previous = pascalSmooth(n-1)
    for i in range(n):
        if i ==0:
            #恒等于 1
            pascalDiff[0][i] = pascalSmooth_previous[0][i]
        elif i == n-1:
            #恒等于 -1
            pascalDiff[0][i] = -pascalSmooth_previous[0][i-1]
        else:
            pascalDiff[0][i] = pascalSmooth_previous[0][i] - pascalSmooth_previous[0][i-1]
    return pascalDiff
#通过平滑系数和差分系数的卷积运算计算卷积核
def getSobelKernel(winSize):
     pascalSmoothKernel = pascalSmooth(winSize)
     pascalDiffKernel = pascalDiff(winSize)
     #水平方向上的卷积核
     sobelKernel_x = signal.convolve2d(pascalSmoothKernel.transpose(),pascalDiffKernel,mode='full')
     #垂直方向上的卷积核
     sobelKernel_y = signal.convolve2d(pascalSmoothKernel,pascalDiffKernel.transpose(),mode='full')
     return (sobelKernel_x,sobelKernel_y)
# sobel 边缘检测
def sobel(image,winSize):
    rows,cols = image.shape
    pascalSmoothKernel = pascalSmooth(winSize)
    pascalDiffKernel = pascalDiff(winSize)
    # --- 与水平方向的卷积核卷积 ----
    image_sobel_x = np.zeros(image.shape,np.float32)
    #垂直方向上的平滑
    image_sobel_x = signal.convolve2d(image,pascalSmoothKernel.transpose(),mode='same')
    #水平方向上的差分
    image_sobel_x = signal.convolve2d(image_sobel_x,pascalDiffKernel,mode='same')
    # --- 与垂直方向上的卷积核卷积 --- 
    image_sobel_y = np.zeros(image.shape,np.float32)
    #水平方向上的平滑
    image_sobel_y = signal.convolve2d(image,pascalSmoothKernel,mode='same')
    #垂直方向上的差分
    image_sobel_y = signal.convolve2d(image_sobel_y,pascalDiffKernel.transpose(),mode='same')
    return (image_sobel_x,image_sobel_y)
#主函数
if __name__ =="__main__":
    if len(sys.argv)>1:
        image = cv2.imread(sys.argv[1],cv2.CV_LOAD_IMAGE_GRAYSCALE)
    else:
        print "Usge:python Sobel.py imageFile"
    #得到卷积核
    sobelKernel3 = getSobelKernel(3)
    sobelKernel5 = getSobelKernel(5)
    print(sobelKernel3)
    print(sobelKernel5)
    #卷积
    image_sobel_x,image_sobel_y = sobel(image,3)
    edge_x = np.abs(image_sobel_x)
    edge_x[ edge_x>255]=255
    edge_x=edge_x.astype(np.uint8)
    edge_y = np.abs(image_sobel_y)
    edge_y[ edge_y>255]=255
    edge_y=edge_y.astype(np.uint8)
    cv2.imwrite("img7_sobel_x_3_3.jpg",edge_x)
    cv2.imwrite("img7_sobel_y_3_3.jpg",edge_y)
    #边缘强度:两个卷积结果对应位置的平方和
    edge = np.sqrt(np.power(image_sobel_x,2.0) + np.power(image_sobel_y,2.0))
    #边缘强度的灰度级显示
    edge[edge>255] = 255
    edge = np.round(edge)
    edge = edge.astype(np.uint8)
    cv2.imshow("sobel edge",edge)
    cv2.imwrite("sobel.jpg",edge)
    #模拟素描
    pencilSketch = edge.copy()
    pencilSketch = 255 - pencilSketch
    pencilSketch[pencilSketch < 80] = 80
    cv2.imshow("pencilSketch",pencilSketch)
    cv2.imwrite("pencilSketch.jpg",pencilSketch)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值