(十七) 图像金字塔
图像金字塔是图像多尺度表达的一种,是一种以多分辨率来解释图像的有效但概念简单的结构。一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。
图像金字塔是图像中多尺度表达的一种,最主要用于图像的分割,是一种以多分辨率来解释图像的有效但概念简单的结构。
图像金字塔最初用于机器视觉和图像压缩,一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。
金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。
我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低
图像金字塔类型
高斯金字塔(Gaussianpyramid): 用来向下采样,主要的图像金字塔
拉普拉斯金字塔(Laplacianpyramid): 用来从金字塔低层图像重建上层未采样图像,在数字图像处理中也即是预测残差,可以对图像进行最大程度的还原,配合高斯金字塔一起使用。
两者的简要区别:高斯金字塔用来向下降采样图像,而拉普拉斯金字塔则用来从金字塔底层图像中向上采样重建一个图像。
要从金字塔第i层生成第i+1层(我们表示第i+1层为G_i+1),我们先要用高斯核对G_1进行卷积,然后删除所有偶数行和偶数列。当然的是,新得到图像面积会变为源图像的四分之一。按上述过程对输入图像G_0执行操作就可产生出整个金字塔。
对图像向上采样:pyrUp函数
对图像向下采样:pyrDown函数
import cv2 as cv
def pyramin(img):
"""高斯金字塔"""
# 图像金字塔层数
level = 3
# 复制图片
tmp = img.copy()
pyramin_img = []
for i in range(level):
dst = cv.pyrDown(tmp)
pyramin_img.append(dst)
cv.imshow("pyramid_down_" + str(i), dst)
tmp = dst.copy();
return pyramin_img
def lapalian(img):
"""拉普拉斯金字塔"""
# 拉普拉斯需要用到高斯金字塔结果
pyramid_images = pyramin(img)
level = len(pyramid_images)
# 从高到低进行循环
for i in range(level - 1, -1, -1):
if (i - 1) < 0:
# 如果是第一幅图,则用原图进行计算
exapand = cv.pyrUp(pyramid_images[i], dstsize=img.shape[:2])
lpls = cv.subtract(img, exapand)
cv.imshow("lpls_down_" + str(i), lpls)
else:
exapand = cv.pyrUp(pyramid_images[i], dstsize=pyramid_images[i - 1].shape[:2])
lpls = cv.subtract(pyramid_images[i - 1], exapand)
cv.imshow("lpls_down_" + str(i), lpls)
# 图像长宽必须是2的倍数,即2的n次方,或者是一个宽高相等的图片
src = cv.imread('images/timg.jpg')
cv.imshow("原图", src)
lapalian(src)
cv.waitKey(0)
cv.destroyAllWindows()
注意:
我们选用的图片大小必须是2^n大小,或者是一个宽高相等的图片。
(十八)图像梯度
可以把图像看成二维离散函数,图像梯度其实就是这个二维离散函数的求导:
图像梯度: G(x,y) = dx i + dy j;
dx(i,j) = I(i+1,j) - I(i,j);
dy(i,j) = I(i,j+1) - I(i,j);
其中,I是图像像素的值(如:RGB值),(i,j)为像素的坐标。
图像梯度一般也可以用中值差分:
dx(i,j) = [I(i+1,j) - I(i-1,j)]/2;
dy(i,j) = [I(i,j+1) - I(i,j-1)]/2;
图像边缘一般都是通过对图像进行梯度运算来实现的。
图像梯度的最重要性质是,梯度的方向在图像灰度最大变化率上,它恰好可以反映出图像边缘上的灰度变化
上面说的是简单的梯度定义,其实还有更多更复杂的梯度公式。
import cv2 as cv
def sobel(img):
"""索贝尔算子"""
grad_x = cv.Sobel(img, cv.CV_32F, 1, 0)
grad_y = cv.Sobel(img, cv.CV_32F, 0, 1)
gradx = cv.convertScaleAbs(grad_x)
grady = cv.convertScaleAbs(grad_y)
cv.imshow("x", gradx)
cv.imshow("y", grady)
gradxy = cv.addWeighted(gradx, 0.5, grady, 0.5, 0)
cv.imshow("grad", gradxy)
def scharr(img):
"""某些边缘差异很小的情况下使用"""
grad_x = cv.Scharr(img, cv.CV_32F, 1, 0)
grad_y = cv.Scharr(img, cv.CV_32F, 0, 1)
gradx = cv.convertScaleAbs(grad_x)
grady = cv.convertScaleAbs(grad_y)
cv.imshow("x", gradx)
cv.imshow("y", grady)
gradxy = cv.addWeighted(gradx, 0.5, grady, 0.5, 0)
cv.imshow("grad", gradxy)
def lapalian(img):
"""拉普拉斯算子"""
dst = cv.Laplacian(img, cv.CV_32F)
lpls = cv.convertScaleAbs(dst)
cv.imshow("lpls", lpls)
src = cv.imread('images/template.jpg')
lapalian(src)
cv.waitKey(0)
cv.destroyAllWindows()
图像梯度
(十九)Canny边缘提取
图像的边缘检测的原理是检测出图像中所有灰度值变化较大的点,而且这些点连接起来就构成了若干线条,这些线条就可以称为图像的边缘。
canny 算法五步骤
高斯模糊
灰度转换
计算梯度
非最大信号抑制
高低阈值输出二值图像
import cv2 as cv
def edge(img):
# 高斯模糊,降低噪声
blurred = cv.GaussianBlur(img, (3, 3), 0)
# 灰度图像
gray = cv.cvtColor(blurred, cv.COLOR_RGB2GRAY)
# 图像梯度
xgrad = cv.Sobel(gray, cv.CV_16SC1, 1, 0)
ygrad = cv.Sobel(gray, cv.CV_16SC1, 0, 1)
# 计算边缘
# 50和150参数必须符合1:3或者1:2
edge_output = cv.Canny(xgrad, ygrad, 50, 150)
# 图一
cv.imshow("edge", edge_output)
dst = cv.bitwise_and(img, img, mask=edge_output)
# 图二(彩色)
cv.imshow('cedge', dst)
src = cv.imread('images/template.jpg')
# 图三(原图)
cv.imshow('def', src)
edge(src)
cv.waitKey(0)
cv.destroyAllWindows()
Canny边缘提取
(二十)直线检测
霍夫变换
Hough变换是经典的检测直线的算法。其最初用来检测图像中的直线,同时也可以将其扩展,以用来检测图像中简单的结构。
OpenCV提供了两种用于直线检测的Hough变换形式。其中基本的版本是cv2.HoughLines。其输入一幅含有点集的二值图(由非0像素表示),其中一些点互相联系组成直线。通常这是通过如Canny算子获得的一幅边缘图像。cv2.HoughLines函数输出的是[float, float]形式的ndarray,其中每个值表示检测到的线(ρ , θ)中浮点点值的参数。
函数将通过步长为1的半径和步长为π/180的角来搜索所有可能的直线。
import cv2 as cv
import numpy as np
def line_detection(img):
"""方法一"""
gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
edges = cv.Canny(gray, 50, 150, apertureSize=3)
lines = cv.HoughLines(edges, 1, np.pi / 180, 200)
# 以下为标准做法
for line in lines:
rho, theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0 + 1000 * (-b))
y1 = int(y0 + 1000 * a)
x2 = int(x0 - 1000 * (-b))
y2 = int(y0 - 1000 * a)
cv.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv.imshow("img lines", img)
def line_detect_possible(img):
"""方法二"""
gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
edges = cv.Canny(gray, 50, 150, apertureSize=3)
# minLineLength:线段最大长度
# maxLineGap:点和线段之间允许的间隔大小
lines = cv.HoughLinesP(edges, 1, np.pi / 180, 200, minLineLength=50, maxLineGap=10)
for line in lines:
x1, y1, x2, y2 = line[0]
cv.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv.imshow("img lines", img)
src = cv.imread('images/driver.jpg')
cv.imshow('def', src)
# line_detection(src)
line_detect_possible(src)
cv.waitKey(0)
cv.destroyAllWindows()
直线检测
直线检测
网友评论