图像变换有什么用?
图像变换意义:
- 图像的特征更为突出
- 原来无法直接观测的特征直接显现出来
- 需要提取图像中的特征,便于后续处理及图像理解
常见图像变换:
- 几何变换:图像放缩、图像平移、图像旋转、图像镜像、图像翻转。
- 距离变换:通常作用于二值图像上,描述的是该像素点到最近的区域边界的距离。如果假设背景为黑(对应值为0),则为到最近值0的距离。
[00000000011111100111111001111110011111100111111000000000][00000000011111100122221001233210012222100111111000000000]\begin{bmatrix} 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 1 & 1 & 1 & 1 & 1 & 0 \\ 0 & 1 & 1 & 1 & 1 & 1 & 1 & 0 \\ 0 & 1 & 1 & 1 & 1 & 1 & 1 & 0 \\ 0 & 1 & 1 & 1 & 1 & 1 & 1 & 0 \\ 0 & 1 & 1 & 1 & 1 & 1 & 1 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \end{bmatrix} \begin{bmatrix} 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 1 & 1 & 1 & 1 & 1 & 0 \\ 0 & 1 & 2 & 2 & 2 & 2 & 1 & 0 \\ 0 & 1 & 2 & 3 & 3 & 2 & 1 & 0 \\ 0 & 1 & 2 & 2 & 2 & 2 & 1 & 0 \\ 0 & 1 & 1 & 1 & 1 & 1 & 1 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡00000000111110011111001111100111110011111001111100000000⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡00000000111110012221001232100123210012221001111100000000⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤
距离变换可以将联通的区域分开,比如数硬币。 - 对数极坐标变换(Log-Polar变换)
将图像像素坐标转换为极坐标,然后对距离取对数:
变换公式:
{r=x2+y2,ρ=log(r);θ=arctanyx\left\{ r = \sqrt{x^2 + y^2}, \rho = log(r); \\ \theta = arctan\frac{y}{x} \right. {r=x2+y2,ρ=log(r);θ=arctanxy
反变换公式:
{x=rcos(θ);y=rsin(θ)\left\{ x = rcos(\theta); \\ y = rsin(\theta) \right. {x=rcos(θ);y=rsin(θ)
Log-Polar变换应用:全景展开
总结: - 图像几何变换包括放缩、旋转、镜像等
- 距离变换具有类似“细化”的效果
- Log-Polar变换将直角坐标变换到极坐标,可用于全景展开等应用
灰度直方图
目标:
- 掌握直方图的基本概念
- 掌握从直方图判断图像质量的方式
- 掌握直方图均衡的基本思想
灰度越集中,对比度越低;灰度平均,对比度高,但噪声也多。
直方图均衡化
- 直方图均衡化的作用就是图像增强
需要满足的条件:亮的依然亮,暗的依然暗。 - 直方图映射方法:
sk=∑j=0knjn,k=0,1,2,⋅⋅⋅,L−1s_k = \sum_{j=0}^{k}{\frac{n_j}{n}},k = 0,1,2,···,L-1 sk=j=0∑knnj,k=0,1,2,⋅⋅⋅,L−1
n为总像素个数,L表示所有像素值的种类数,表示新的灰度值所对应的概率,等于之前所有灰度值概率的累加。
示例:
255 | 128 | 200 | 50 |
---|---|---|---|
50 | 200 | 255 | 50 |
255 | 200 | 128 | 128 |
200 | 200 | 255 | 50 |
、
灰度值 | 像素个数 | 概率 | 累计概率 | 根据函数映射后的灰度值 | 取整 |
---|---|---|---|---|---|
50 | 4 | 0.25 | 0.25 | 0.25*(255-0) = 63.75 | 64 |
128 | 3 | 0.1875 | 0.4375 | 111.5625 | 112 |
200 | 5 | 0.3125 | 0.75 | 191.25 | 191 |
255 | 4 | 0.25 | 1 | 255 | 255 |
、
255 | 112 | 191 | 64 |
---|---|---|---|
64 | 191 | 255 | 64 |
255 | 191 | 112 | 112 |
191 | 191 | 255 | 64 |
全局直方图均衡化会增加噪声。
局部直方图均衡化
在每一个小邻域内使用直方图均衡化;
利用局部直方图统计进行灰度增强
- 在图像的每个小邻域内,计算局部直方图,进而计算灰度和方差;
- 根据需求,对图像的亮部或暗部进行增强。如对暗部增强方式如下:
- 计算局部均值mlm_lml与全局均值mmm,如ml≤k0mm_l \leq k_0mml≤k0m,则认为该点是候选点;
- 进一步计算局部方差σl\sigma_lσl与全局方差σ\sigmaσ,如σl≤k2σ\sigma_l\leq k_2\sigmaσl≤k2σ,且k1σ≤σl,k1<k2k_1\sigma\leq\sigma_l,k_1<k_2k1σ≤σl,k1<k2(对标准差为0的区域不增强),则满足增强条件;
- 对同时满足1,2条件的点进行增强:
g(x,y)=E⋅f(x,y)g(x,y) = E \cdot f(x,y) g(x,y)=E⋅f(x,y)
需要指定k0,k1,k2k_0,k_1,k_2k0,k1,k2
总结:
- 直方图是对图像中的像素灰度进行统计;
- 通过直方图可判断图像曝光及对比度等情况;
- 直方图均衡可增强图像对比度;
- 适当使用局部直方图均衡可有效改善图像暗部质量
霍夫变换
目标:
- 掌握参数空间及霍夫(Hough)变换的基本原理
- 了解Hough变换的基本步骤
Hough变换
- 问题:给定图像中的n个点,希望找到这些点中的多个子集,每个子集对应一条直线。
- 难点:我们不知道图像中哪些点对应一条直线
- 暴力搜索:首先得到n(n−1)2\frac{n(n-1)}{2}2n(n−1)条直线,进一步对所有附近的点进行比较,判断是否属于直线。因此需要执行n(n−1)2\frac{n(n-1)}{2}2n(n−1)次比较,计算量太大!
- 此外,判断点是否属于直线需要借助阈值,该值难以设置。
- Hough于1962年提出Hough变换,很好的解决了计算速度和鲁棒性的问题
- 核心思想:将原坐标(x,y)转换到参数空间中表示,进一步利用投票机制解决鲁棒性问题
- 直线方程表达:
- 斜距/截距:y=kx+by = kx +by=kx+b,每一条直线对应一个k,bk,bk,b
- 法线式:xcosθ+ysinθ=ρxcos\theta + ysin\theta = \rhoxcosθ+ysinθ=ρ,每一条直线对应一个ρ,θ\rho,\thetaρ,θ
- 直角坐标系的一点(x0,y0x_0,y_0x0,y0),对应参数空间(θ,ρ\theta,\rhoθ,ρ)中的一条近似正弦曲线:
x0cosθ+y0sinθ=ρ⇒ρ=xo2+yo2cos(θ+ϕ)x_0cos\theta + y_0sin\theta = \rho \Rightarrow \rho = \sqrt{{x_o}^2+{y_o}^2}cos(\theta + \phi) x0cosθ+y0sinθ=ρ⇒ρ=xo2+yo2cos(θ+ϕ) - 同一条直线上的多个点,在(θ,ρ)(\theta,\rho)(θ,ρ)空间中必相交于一点,但(θ,ρ)(\theta,\rho)(θ,ρ)空间中的曲线是非线性的,直接寻找交汇于一点的曲线计算量大,因此将(θ,ρ)(\theta,\rho)(θ,ρ)空间划分网格,寻找包含曲线数目多的网格点。
Hough变换步骤
- 将(θ,ρ)(\theta,\rho)(θ,ρ)空间量化为许多小格
- 根据x−yx-yx−y平面每一个点代入θ\thetaθ的量化值。算出各个ρ\rhoρ
- 将对应格内通过ρ−θ\rho-\thetaρ−θ曲线的数目计数累加
- 当全部点变换后,对小格进行检验(投票机制)。设置累计阈值TTT,计数器大于TTT的小格对应于共线点,其可以用作直线拟合参数。小于TTT的反映非共线点,丢弃不用。
应用:寻找飞机跑道,对原视图像进行边缘检测,提取边缘点,寻找共线的点即飞机跑道。
总结:
- 霍夫变换基于直线法线表示及对应的参数空间(θ,ρ)(\theta,\rho)(θ,ρ);
- 最终直线参数确定采用投票机制(即避免了复杂的方程求解,又对噪声具有一定鲁棒性),统计参数空间小格中的曲线数目。
图像变换实战演练
目标
- 使用OpenCV实现基本图像几何变换
- 使用OpenCV实现距离变换和Log-Polar变换
- 使用OpenCV实现直方图计算与直方图均衡
- 使用OpenCV实现Hough变换
相关函数
- 距离变换
dst = cv2.distanceTransform(src, distanceType, maskSize[, dst[, dstType]])
#distanceType:距离计算方式,DIST_L1,DIST_L2或DIST_C;
#maskSize:掩模尺寸,可取DIST_MASK_PRECISE或DIST_MASK_3,5等。
- Log-Polar变换
dst = cv2.logPolar(src, center, M, flags[, dst])
#center:变换中心点
#M:幅值尺度参数
#flags:标志。是插值方法和下面选项的组合:CV_WARP_FILL_OUTLIERS填充目标图像中的所有像素;CV_WARP_INVERSE_MAP表示矩阵是从目标图像到源图像的反变换
- 计算直方图
matplotlib.pyplot.hist(x, bins=None, range=None,...)
#bins:多少个柱;
#range:显示的范围
- 直方图均衡化
dst = cv2.equalizeHist(src[, dst])
- 标准Hough变换
lines = cv.HoughLines(image, rho, theta, threshold[, lines[, srn[, stn[, min_theta[, max_theta]]]]])
#image:输入图像,应为二值图像,通常使用边缘检测结果
#rho:线段以像素为单位的距离精度,double类型的,推荐用1.0
#theta:线段以弧度为单位的角度精度,推荐用numpy.pi/180
#threshold:累加平面的阈值参数,int类型,超过设定阈值才被检测出线段,值越大,基本意味着检出的线段越长,检出的线段个数越少。
- 累计概率Hough变换
lines = cv2.HoughLinesP(image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]])
实战演练
程序功能:
- 实现基本图像几何变换:放缩、翻转
- 实现距离变换和Log-Polar变换
- 实现直方图计算与直方图均衡
- 实现Hough变换
- 以上如果显示
import cv2
import matplotlib.pyplot as plt
import numpy as npimg = cv2.imread('C:/python/img/lena.jpg')
cv2.imshow("Source", img)#实现图像改变大小和翻转
w, h = img.shape[0:2]
resized = cv2.resize(img, (int(w/4), int(h/2)))
flipped = cv2.flip(img, -1)
cv2.imshow("Resized", resized)
cv2.imshow("Flipped", flipped)#实现图像的距离变换
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thr = cv2.threshold(gray, 100, 255, cv2.THRESH_OTSU)
dist = cv2.distanceTransform(thr, cv2.DIST_L2, cv2.DIST_MASK_3) #3*3的掩模
dist_norm = cv2.convertScaleAbs(dist)
cv2.imshow("Distance transform", dist_norm)#实现Log-polar变换
center = (w/2, h/2)
maxRadius = 0.7*min(center)
M = w/cv2.log(maxRadius)
print(maxRadius, M[0])
log_polar = cv2.logPolar(img, center, M[0]*0.8, cv2.INTER_LINEAR + cv2.WARP_FILL_OUTLIERS)
cv2.imshow("Log-polar", log_polar)#实现灰度直方图和直方图均衡化
plt.hist(gray.ravel(), 255, [0,256])
plt.show()
equa = cv2.equalizeHist(gray)
cv2.imshow("Equalized image", equa)#实现Hough变换
edges = cv2.Canny(thr, 50, 150)
disp_edge = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
lines = cv2.HoughLinesP(edges, 1, 1*np.pi/180, 10)
for line in lines:for x1, y1, x2, y2 in line:#画出直线cv2.line(disp_edge, (x1,y1), (x2,y2), (0, 255, 0), 1)passprint("Line count:", len(lines))
cv2.imshow("edges",edges)
cv2.imshow("Hough lines", disp_edge)cv2.waitKey()
cv2.destroyAllWindows()