图像的变换

    一、旋转

       

    旋转的数学原理:
    经过计算,可以得到以下关系:

    x′=xcosθ−ysinθ
    y′=xsinθ+ycosθ 
    写成矩阵的形式是:

    \begin{bmatrix} x' \\ y' \\1\end{bmatrix} = \begin{bmatrix} cos\theta & -sin\theta &0\\ sin\theta & cos\theta & 0 \\ 0 & 0 & 1\end{bmatrix} * \begin{bmatrix} x \\ y\\1 \end{bmatrix}

    或者是:(注意坐标按行的方式和按列的方式排列时,变换矩阵的左右位置不一样,矩阵中元素的位置也不一样。可以根据计算的实际过程来确定)

    \begin{bmatrix} x' & y' & 1 \end{bmatrix} = \begin{bmatrix} x & y& 1 \end{bmatrix} * \begin{bmatrix} cos\theta & sin\theta & 0\\ -sin\theta & cos\theta & 0\\0 & 0 & 1 \end{bmatrix}

   因此,如果知道旋转的角度,可以直接构造矩阵进行变换,代码如下:(当角度为正时,逆时针旋转)。

src = np.array([[5, 5], [5, 10], [10, 5], [10, 10]])
angle = 10
angle_pi = angle * np.pi / 180
a = np.cos(angle_pi)
b = np.sin(angle_pi)
A =np.array([[a, b], [-b, a]])
dest = np.dot(src, A)

    如果是多边形的话,旋转后会形变,如矩形会变形为平行四边形,变换前后如下图所示:

   

    对于图像的变换,可以使用opencv等工具进行变换:

rotate_matrix = cv2.getRotationMatrix2D(center=(img.shape[0]/2, img.shape[1]/2), angle=90, scale=1)
rot_img = cv2.warpAffine(img, rotate_matrix, (img.shape[0], img.shape[1]))

    上述过程是绕原点旋转,如果想绕任意一点(x0, y0)旋转,可以先把(x0, y0)平移到原点(例如把所有点都减去(x0, y0)),再进行旋转。

    二、位移

    位移比较简单,就是简单坐标加减过程    

    \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} = \begin{bmatrix} x \\ y \\ 1\end{bmatrix} + \begin{bmatrix} dx \\ dy \\ 0 \end{bmatrix}

    用变换矩阵进行计算就是:

    \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} = \begin{bmatrix} 1 & 0 & dx \\ 0 & 1 & dy\\ 0 & 0 & 1 \end{bmatrix} * \begin{bmatrix} x \\ y \\ 1\end{bmatrix}

    图像的平移:需构造变换矩阵

trans_matrix = np.array([[1, 0, 10],[0, 1,  5]], dtype=np.float32)
trans_img = cv2.warpAffine(img, trans_matrix, (img.shape[0] + 10, img.shape[1] + 5))

    三、缩放

    直接对原坐标进行缩放,图像的中心点会偏移

    \begin{bmatrix} x'\\ y'\\ 1 \end{bmatrix} = \begin{bmatrix} sx & 0 & 0\\ 0 & yx & 0\\ 0 & 0 & 1 \end{bmatrix} * \begin{bmatrix} x\\ y\\ 1 \end{bmatrix}

   如果想保持中心点不变,可以先把中心点平移到原点,再进行缩放,然后再平移回来

    \begin{bmatrix} x'\\ y'\\ 1 \end{bmatrix} = \begin{bmatrix} sx & 0 & 0\\ 0 & yx & 0\\ 0 & 0 & 1 \end{bmatrix} * \left ( \begin{bmatrix} x\\ y\\ 1 \end{bmatrix} - \begin{bmatrix} x0\\ y0\\ 0 \end{bmatrix} \right ) + \begin{bmatrix} x0\\ y0\\ 0 \end{bmatrix}

    或者利用矩阵来计算:同样的过程,先平移,再缩放,再平移:(注意:根据矩阵乘法结合律,离原坐标近的矩阵先执行)

    \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} = \begin{bmatrix} 0 & 0 & x0 \\ 0 & 0 & y0 \\ 0 & 0 & 1 \end{bmatrix} * \begin{bmatrix} sx & 0 & 0 \\ 0 & sy & 0 \\ 0 & 0 & 1 \end{bmatrix} * \begin{bmatrix} 0 & 0 & x0 \\ 0 & 0 & y0 \\ 0 & 0 & 1 \end{bmatrix} * \begin{bmatrix} x \\ y \\ 1 \end{bmatrix}

    图像的缩放:

resize_img = cv2.resize(src=img, dsize=(img.shape[1]*2, img.shape[0]*2), interpolation=cv2.INTER_LINEAR)

    四、仿射变换

    把旋转、平移、缩放、对称、错切等结合起来,就是仿射变换,仿射变换不会改变图形的相对关系,平行线变换后还是平行线,圆变换后变成椭圆。其中的变换矩阵就是按照变换的顺序,把各个过程的变换乘起来(先执行的离原坐标较近)。

    图形的仿射变换:需要知道三对对应点,求变换矩阵M

rows, cols, ch = img.shape
pts1 = np.float32([[0, 0], [cols - 1, 0], [0, rows - 1]])
pts2 = np.float32([[cols * 0.2, rows * 0.1], [cols * 0.9, rows * 0.2], [cols * 0.1, rows * 0.9]])
M = cv2.getAffineTransform(pts1, pts2)
dst_img = cv2.warpAffine(img, M, (cols, rows))

    反过来,利用上面的过程,可以在知道旋转前、后的坐标的情况下,求变换矩阵。例如需要把图像进行变换,使图像中的某些点与目标位置的点进行匹配,从而对原图像的位置进行调整。如根据人脸图像中的眼睛、鼻子、嘴巴等关键点,对人脸进行对齐。   

def bestFit(destination, source):
    destMean = np.mean(destination, axis=0)
    srcMean = np.mean(source, axis=0)

    srcVec = (source - srcMean).flatten()
    destVec = (destination - destMean).flatten()

    a = np.dot(srcVec, destVec) / np.linalg.norm(srcVec)**2 #dot(a, b) = |a|*|b|*cos
    b = 0
    for i in range(destination.shape[0]): 
        b += srcVec[2*i] * destVec[2*i+1] - srcVec[2*i+1] * destVec[2*i] #|a x b| = |a||b|sin, |a x b| = x1y2 - x2y1
    b = b / np.linalg.norm(srcVec)**2 #sin
    
    A = np.array([[a, b], [-b, a]]) #[[cos, sin], [-sin, cos]]
    srcMean = np.dot(srcMean, T)
    t = destMean - srcMean
    return A, t

    其中,destination是一组目标点,source是原图像的对应点,上面代码求的是平均的变换。返回的A是旋转矩阵,t 是平移矩阵。

    得到A和t之后,如果是对其他的坐标点进行变换,方法如下:

dest = np.dot(src, A) + t

    如果是对图像进行变换,方法如下:

A2 = np.linalg.inv(A) #逆矩阵
t2 = np.dot(-t, A2) #位移矩阵也需要进行旋转和转置
outImg = ndimage.interpolation.affine_transform(img, A2, t2[[1, 0]], output_shape=self.imgSize)

  五、透视变换

    透视变换相当于将图像投影到一个新的平面(可能与原平面不平行),得到的是从原图到目标平面的映射,直线变换后还是直线,但平行线变换后不一定还保持平行。透视变换需要确定四对坐标。   

points1 = np.float32([ [480, 140], [0,140], [0,0], [480,0] ]) # 原图象四个角坐标
points2 = np.float32([ [480, 200], [0, 150], [0, 50], [480, 0]]) # 目标图像四个角坐标
M = cv2.getPerspectiveTransform(points1, points2)
perspective_img = cv2.warpPerspective(img_plate, M, (480, 200)) 

    变换的结果如下:

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值