目录
一,仿射变换
仿射变换的映射方式有两种,前向映射和反向映射。
1,前向映射
扫描原图的每个像素(v,w),变换后的值填入对应的(x,y)处。
对于多个像素点映射到同一个(x,y)的情况,不太好处理,所以反向映射更有效。
2,反向映射
扫描变换后图像的每个像素(x,y),根据逆变换找到原图中对应的(v,w)
大概率找到的不是整点,所以需要使用图像插值 https://blog.csdn.net/nameofcsdn/article/details/118281568
二,常见的仿射变换
1,恒等变换
2,尺度变换(缩放)
3,旋转变换
4,平移变换
5,偏移变换
垂直偏移变换:
水平偏移变换:
三,透视投影
把三维图像透视投影到二维平面:
x' = x/z , y' = y/z
如,有3个垂直于z轴的正方形,分别是:
{6<=x<=12,6<=y<=12,z=1}、{6<=x<=12,6<=y<=12,z=2}、{6<=x<=12,6<=y<=12,z=3}
那么投影之后得到的正方形如图:
这么看着没什么感觉,但是如果旋转180度,再添加2条透视线:
显然,原点O就是透视点,对应z=∞的点。
四,透视变换
透视变换就是,把一个平面图形在三维空间中做仿射变换,然后再做透视投影变成平面图形。
三维仿射变换:
其中w可以取任意不为0的数,如w=1
然后透视投影:
注意到,这9个变量同时放大缩小是不影响的,所以不妨设a33 = 1,那么就剩下8个未知数。
五,图像配准
输入两幅图片,已知一副图像是另外一副图像变换得到,通过计算反推出变换。
常用的方法就是根据特征点,代入图像变换的公式求解。
特征点的最少数目,取决于变换的方式。而特征点越多,自然配准越容易,越准确。
PS:只有部分重合的图片,也能做配准,如全景照片就是配准之后拼接起来的。
六,Opencv 配准+变换
从未知数的数量来看,仿射变换有6个未知数,透视变换有8个未知数。
所以确定一个仿射变换需要3个点的前后坐标值,确定一个透视变换需要4个点的前后坐标值。
从几何意义来看,仿射变换是平面操作,所以需要3个点,透视变换是空间操作,所以需要4个点。
Opencv里面提供了仿射变换和透视变换的配准和变换的接口,先提供点的坐标匹配,根据坐标匹配自动配准,得到变换公式的对象,再用这个对象对整个图像进行变换。
仿射变换:(这里是旋转的示例)
image = cv2.imread("D:/im.jpg", 0)
image = cv2.resize(image,(500,500))
cv2.imshow("img",image)
p_src = numpy.float32([[0, 0],[0, 500],[500, 0]])
p_dst = numpy.float32([[300, 0],[0, 400],[700, 300]])
trans = cv2.getAffineTransform(p_src,p_dst)
image2 = cv2.warpAffine(image,trans,(700,700))
cv2.imshow("img2",image2)
cv2.waitKey(0)
变换结果:
透视变换:
image = cv2.imread("D:/im.jpg", 0)
image = cv2.resize(image,(500,500))
cv2.imshow("img",image)
p_src = numpy.float32([[0, 0],[0, 500],[500, 0],[500,500]])
p_dst = numpy.float32([[0, 0],[0, 500],[250, 0],[500,500]])
trans = cv2.getPerspectiveTransform(p_src,p_dst)
image2 = cv2.warpPerspective(image,trans,(500,500))
cv2.imshow("img2",image2)
cv2.waitKey(0)
变换结果:
七,其他变换
1,双线性变换
显然这个也是需要4个点才能确定变换。
书中的这个式子卡住了我很久,因为我一直想不通这是什么变换,于是我举了一个简单的例子:
把正方形{(0,0)(0,500)(500,0){500,500}}变换为梯形{(0,0)(0,250)(500,0){500,500}}
如果是透视,那么变换就是x=500u/(1000-u), y=500v/(1000-u)
如果是双线性变换,那么就是x=u, y=v(u+500)/1000
2,实现双线性变换和透视变换
代码:
image = cv2.imread("D:/im.jpg", 0)
image = cv2.resize(image,(500,500))
cv2.imshow("img",image)
image2 = image.copy()
image3 = image.copy()
for i in range(image.shape[0]):
for j in range(image.shape[1]):
image2[i, j] = 0
image3[i, j] = 0
for i in range(image.shape[0]):
for j in range(image.shape[1]):
image2[int(i * 500 / (1000 - i)) % image.shape[0], int(j * 500 / (1000 - i)) % image.shape[1]] = image[i, j]
image3[i,int(j*(i+500)/1000)%image.shape[1]] = image[i,j]
cv2.imshow("image2",image2) #透视
cv2.imshow("image3",image3) #双线性
cv2.waitKey(0)
运行:
可以看出来,图2是透视,物体在图片中的高度位置有变化,直线还是直线,
图3是双线性变换,高度没有变化,直线变得不直了。
PS:自己写的透视变换有点粗糙,中间有些黑线,如果是反向映射的话应该就不会这样了:
image = cv2.imread("D:/im.jpg", 0)
image = cv2.resize(image,(500,500))
cv2.imshow("img",image)
image2 = image.copy()
image3 = image.copy()
for i in range(image.shape[0]):
for j in range(image.shape[1]):
image2[i, j] = 0
image3[i, j] = 0
for i in range(image.shape[0]):
for j in range(image.shape[1]):
j2 = int(j * 1000 / (i + 500))
if j2 >= 500:
image2[i, j] = 0
else:
image2[i,j] = image[int(i * 1000 / (i + 500)) % image.shape[0], j2]
image3[i,int(j*(i+500)/1000)%image.shape[1]] = image[i,j]
cv2.imshow("image2",image2) #透视
cv2.imshow("image3",image3) #双线性
cv2.waitKey(0)