今天的简单,利用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()