图像梯度-Sobel算子
图中黑色部分像素值为0,白色部分像素值为255,则黑白交界处边缘处就存在梯度。
但是在像素点层面上如何进行梯度的计算呢?
在进行梯度计算时要考虑两个方向:水平和竖直
dst = cv2.Sobel(src, ddepth, dx, dy, ksize)
ddepth:图像的深度
dx和dy分别表示水平和竖直方向
ksize是Sobel算子的大小,代表指定的核的大小。
img=cv2.imread('pie.png',cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
cv_show(sobelx,'sobelx')
白到黑是整数,黑到白就是负数了,所有的负数都会被截断成0,所以要取绝对值。
第一次检测的结果(只有边界上会有梯度):
但是只有左半部分,没有右半部分。 因为右边相减是负数,被截断成了0,黑色显示不出来。
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobely = cv2.convertScaleAbs(sobely)
sob=np.hstack((sobelx,sobely))
cv_show(sob,"sob")
在进行了取绝对值操作后,我们可以看到下边的效果图中右半部分显出来了。
sobelxy=cv2.Sobel(img,cv2.CV_64F,1,1,ksize=3)
sobelxy = cv2.convertScaleAbs(sobelxy)
cv_show(sobelxy,'sobelxy')
可以看出,sobelx与sobely分开算效果更加完美。
图像梯度-Scharr算子
图像梯度-laplacian算子
不同算子的差异(代码):
img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
scharrx = cv2.Scharr(img,cv2.CV_64F,1,0)
scharry = cv2.Scharr(img,cv2.CV_64F,0,1)
scharrx = cv2.convertScaleAbs(scharrx)
scharry = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx,0.5,scharry,0.5,0)
laplacian = cv2.Laplacian(img,cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)
res = np.hstack((sobelxy,scharrxy,laplacian))
cv_show(res,'res')
从左到右分别是Sobel、Scharr、Laplacian不同算子的效果图。
可以看出Scharr要比Sobel显示的边缘更加多,而Laplacian就显示的更加少了。
Canny边缘检测
使用高斯滤波器,以平滑图像,滤除噪声。
计算图像中每个像素点的梯度强度和方向。
应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。
应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。
通过抑制孤立的弱边缘最终完成边缘检测。
1:高斯滤波器
2.梯度和方向
3.非极大值抑制
4.双阈值检测
img=cv2.imread("lena.jpg",cv2.IMREAD_GRAYSCALE)
v1=cv2.Canny(img,80,150)
v2=cv2.Canny(img,50,100)
res = np.hstack((v1,v2))
cv_show(res,'res')
可以通过改变最大值和最小值来进行调整参数。
图像金字塔
高斯金字塔
拉普拉斯金字塔
高斯金字塔:向下采样方法(缩小)
高斯金字塔:向上采样方法(放大)
img=cv2.imread("AM.png")
print (img.shape)
up=cv2.pyrUp(img)
print (up.shape)
down=cv2.pyrDown(img)
print (down.shape)
(442, 340, 3)
(884, 680, 3)
(221, 170, 3)
pyrUp()放大图像,空白的像素点补0.
pyrDown()缩小图像,将奇数行和列去掉。
放大缩小后会比原图像模糊,因为有像素值丢失。
拉普拉斯金字塔
down=cv2.pyrDown(img)
down_up=cv2.pyrUp(down)
l_1=img-down_up
cv_show(l_1,'l_1')
图像轮廓
cv2.findContours(img,mode,method)
mode:轮廓检索模式
RETR_EXTERNAL :只检索最外面的轮廓;
RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中;
RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;
RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次;
method:轮廓逼近方法
CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。
CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。
img = cv2.imread('contours.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
#传入绘制图像,轮廓,轮廓索引,颜色模式,线条厚度
# 注意需要copy,要不原图会变。。。
draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)#-1表示显示所有轮廓,123表示第123个轮廓
cv_show(res,'res')
轮廓特征:
cnt = contours[0]
#面积
cv2.contourArea(cnt)
#周长,True表示闭合的
cv2.arcLength(cnt,True)
轮廓近似:
轮廓近似,用最少的线来代替,在几个点之间的直线,如果能用更少的点来表示,就采用更少的点。
epsilon = 0.15*cv2.arcLength(cnt,True) #主要进行调参的函数
approx = cv2.approxPolyDP(cnt,epsilon,True)
draw_img = img.copy()
res = cv2.drawContours(draw_img, [approx], -1, (0, 0, 255), 2)
cv_show(res,'res')
图中白色的是原图像,红色的是轮廓近似。
边界矩形
img = cv2.imread('contours.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
x,y,w,h = cv2.boundingRect(cnt)
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
cv_show(img,'img')