几何变化
仿射变化
以下为坐标系的变化
平移
这里写不了直接放图
很好理解,就是建立变化后的坐标与原坐标的关系,还有一般把坐标系进行移动很麻烦,所以等价成点在移动。等到后面遇到复杂的坐标变化时就好理解了。为了快速解决问题,直接记结论,点的变化就是右边表达式的函数,比如这里就是x向右移tx个单位。
再举个例子,函数图像中
上面的方法是我瞎编的,如果有更好的理解方法就不用管他
放大缩小
关于原点进行
用上面的方法很好理解
关于(x0,y0)放大缩小
往已知转化嘛,先坐标变化使原点跑到(x0,y0)处再放缩,最后移回去
画一下图
看个例子
旋转
这个直接看书吧,
然后给出关于某一点进行旋转的坐标变化:
原理和上面一样也是先移到旋转点,再旋转,最后移回去
直接给公式:
计算仿射矩阵
方程法
就是求那个系数矩阵,只用求6个未知数就行了(虽说有9个数,但最后一行没意义)给出变化前的三个点和变化后对应的三个点就行了列个方程,但python,C++给出了现成的计算函数
python:
import cv2 as cv
import numpy as np
src1=np.array([[0,0],[200,0],[0,200]],dtype=np.float32)#必须为float不然下面会报错
src2=np.array([[0,0],[100,0],[0,100]],dtype=np.float32)
print(cv.getAffineTransform(src1, src2))
[[0.5 0. 0. ]
[0. 0.5 0. ]]
函数法
已知变化过程
import cv2 as cv
a=cv.getRotationMatrix2D((1,2),30,0.5)#cv2中给出了绕某点旋转几度(不是弧度制),伸缩几倍(x,y伸缩程度一样)的矩阵的计算函数
print(a)
[[ 0.4330127 0.25 0.0669873]
[-0.25 0.4330127 1.3839746]]
对于x,y伸缩程度不一样的就用dot函数一步一步算矩阵乘法
插值算法:
简介:先介绍一下图像函数,之前一直把图像当成矩阵处理,这里为了方便坐标变化,将图像当成二元函数 I(x,y)(x是第x列,y是第y行),函数值就是矩阵对应位置的值
定义了图像函数,接下来就是坐标变化,但是有有个问题,对应顺序从哪到哪?
先看从原图到变化后的图,对于放大变化会导致变化后有些点(变化后的图对应的区域的点)没有被对应,而且插值法(后面讲)也用不了,因为该点的周围不一定有四个已知邻近点
那么就只能选从变化后的图对应回原图,这样除非对应的点跑到原图的范围外,其余点要么对应整数点(直接取)要么对应非整数点(周围被已知点包围,就可以用插值法)
邻近插值法
名字上可以看出,就是从周围的四个整数点中选离他距离最近的作为它的值
双线性差值法
从点的位置对四个点的值进行加权,这样更精确
第一象限过程如下图所示:
令a=x-[x],1-a=[x]+1-x,这两个是权重。这样就可以算(x,[y]) (x,[y]+1)
对应的值,算法是与点相对的权重乘以这个点的值,然后相加(至于为什么这样,就是里某个点越近,那个点的权重就越大),算出(x,[y]) (x,[y]+1)的之后就可以在y方向上再用一次这样的加权(对(x,[y]) (x,[y]+1)进行)就得到了(x,y)加权后的值,当然先算y方向再算x方向也一样,具体过程如下图:
python实现
warpAffine函数的参数:
import numpy as np
import cv2 as cv
a=cv.imread(r'C:\Users\19583\Desktop\3.png',cv.IMREAD_COLOR)#读取图片
A=np.array([[2,0,20],[0,2,20]],dtype=np.float32)#系数矩阵,先放大两倍,再平移
ret1=cv.warpAffine(a,A,(1000,1000),flags=cv.INTER_LINEAR,borderMode=cv.BORDER_CONSTANT)#双线性插值法,看flag
ret2=cv.warpAffine(a,A,(1000,1000),flags=cv.INTER_NEAREST,borderMode=cv.BORDER_CONSTANT)#邻近插值法
cv.namedWindow('ret1')
cv.namedWindow('ret2')
cv.imshow('ret1',ret1)
cv.imshow('ret2',ret2)
cv.waitKey(0)
从结果可以看出(ret2山那里有锯齿状得东西),双线性插值法比邻近插值法效果要好
投影变化
简介
物体在三维空间中发生旋转称为投影变化,但如果物体是二维的,就可以用二维投影变化来研究。先来张图感受一下:
然后就是我们可以通过用鼠标选定四个点,再给出矫正后这四个点的位置,以此算出变化的矩阵,接下来拿这个矩阵对图像进行操作以达到矫正的效果。
为什么是四个点:
python
import numpy as np
import cv2 as cv
a=cv.imread(r'C:\Users\19583\Desktop\3.png',cv.IMREAD_COLOR)#读取图片
h,w,t=a.shape
src=np.array([[0,0],[w-1,0],[0,h-1],[w-1,h-1]],dtype=np.float32)#类型必须是float
dst=np.array([[50,50],[w/3,50],[50,h-1],[w-1,h-1]],dtype=np.float32)
p=cv.getPerspectiveTransform(src,dst)#返回的是double类型
ret1=cv.warpPerspective(a,p,(w,h),borderMode=cv.BORDER_CONSTANT)
cv.namedWindow('ret1')
cv.namedWindow('ret2')
cv.imshow('ret1',ret1)
cv.imshow('ret2',a)
cv.waitKey(0)
极坐标变化
简介
可以用来矫正圆形物体或被包含在圆形中的物体,来张图感受一下:
1.将笛卡尔坐标转化为极坐标:
给出公式
这里的arctan2是函数,一个传入两个参数的反正切函数,然后theta小于零时加2pi的操作是为了将theta的范围变为0到2pi
python中提供了以极点为原点的变换函数:cartToPolar
import cv2 as cv
import numpy as np
x=np.array([0,1,2,0,1,2,0,1,2],dtype=np.float64)#这个矩阵的形式可以随便改,只要x,y对应
y=np.array([0,0,0,1,1,1,2,2,2],dtype=np.float64)
r,sita=cv.cartToPolar(x,y,angleInDegrees=True)#最后一个参数True是角度制否则是弧度制
print(r,sita,sep='\n')
[[0. ]
[1. ]
[2. ]
[1. ]
[1.41421356]
[2.23606798]
[2. ]
[2.23606798]
[2.82842712]]
[[ 0. ]
[ 0. ]
[ 0. ]
[90. ]
[44.99045563]
[26.56710434]
[90. ]
[63.43289566]
[44.99045563]]
如果像要实现关于某点进行变化可以先把原点移到该点(对上面x
,y进行加或减一个数)再用cartToPolar函数。
极坐标转化为笛卡尔坐标
给出公式:
这个函数也是以(0,0)为变化中心的,要实现以某个点为变化中心可以在
用polarTocart函数后再加减数,这里与上面是反过来的,理解的话,这个好像就是数学中的某个变换,把圆变成直线,直线变成圆,名字想不起来了,啊啊啊啊啊。
运用到图像变化中
书上讲得很清楚了,主要讲了如何增大精度(我感觉1度,1 精度已经可以了),以及变化乘极坐标后的点对应到cart坐标中的点。就不多说了。
python
先看一下numpy中的title函数,它可以实现一个全是a(数或矩阵)的矩阵的快速生成:
import numpy as np
a=np.array([[1,2],[3,4]])
b=np.tile(a,(2,3))#两行三列
print(b)
import cv2 as cv
import numpy as np
I=cv.imread(r'C:\Users\19583\Desktop\3.png',cv.IMREAD_GRAYSCALE)
def polar(I,center,r,theta=(0,360),rstep=1.0,thetastep=360.0/(180*8)):
#得到距离的最小最大范围
minr,maxr = r
#角度的最小范围
mintheta,maxtheta = theta
#得到图像的高,宽
H = int((maxr-minr)/rstep)+1
W = int((maxtheta-mintheta)/thetastep)+1
O = np.zeros((H,W),I.dtype)
#极坐标变换
r = np.linspace(minr,maxr,H)#生成从minr到maxr的H个数
r = np.tile(r,(W,1))
r = np.transpose(r)#转置
theta = np.linspace(mintheta,maxtheta,W)
theta = np.tile(theta,(H,1))#theta是横坐标,所以不用转置
x,y=cv.polarToCart(r,theta,angleInDegrees=True)#获得极坐标对应的cart坐标
#最近邻插值
for i in range(x.shape[0]):
for j in range(x.shape[1]):
px = int(round(x[i][j])+cx)#round:将一个数字四舍五入到十进制数字的给定精度。得到最近的点的坐标
py = int(round(y[i][j])+cy)#+cx,cy是为了平移到变化中心点
if(px>I.shape[0] or py >I.shape[1]):
O[i][j] = 125#灰色
else:
O[i][j] = I[py][px]#被这个坑惨了,它要求I的通道数只能是1
return O
h,w = I.shape[:2]
print(w,h)
#极左标变换的中心
cx,cy = w/2.0,h/2.0+10
print(cx,cy)
cv.circle(I,(int(cx),int(cy)),50,(255.0,0,0),3)
O = polar(I,(cx,cy),(10,50))
#旋转
O = cv.flip(O,0)
#显示原图和输出图像
cv.imshow("I",I)
cv.imshow("O",O)
cv.imwrite("O.jpg",O)
cv.waitKey(0)
效果
讲一下思路构建一个两个二维矩阵坐标存对应变化后的图像的点,点的内容是该点对应的原图中的点(分别放在两个矩阵中),邻近插值法采用了round四舍五入的方式,
然后这个是有现成的函数的
下面有个例子:
送一个函数: