线性代数的运用之图像的旋转

提起线性代数,老师似乎只会告诉我们矩阵的应用范围很广,很重要。但是
一学期学下来,大家似乎也只会做题并不知道矩阵是如何运用的。一开始的我也是很疑惑,所学的知识似乎和实际的运用丝毫挂不上边。比如,以前听人说图像矩阵可以旋转。但是如何旋转,怎么旋转一直都不了解。虽然上网百度网上只是给出了图像旋转的公式,但却没有给出具体的解释。经过一番钻研还是了解了图像旋转的全过程,这里分享给大家,希望对大家有所帮助。

图像旋转的步骤

首先,大家要先了解图像旋转的步骤有哪些,只有知道自己每一步在做什么才不致于迷茫:
我们先读取一张图片,想象将图片放入一个坐标系中,图片的左上角与坐标原点重合,这时候的图片矩阵的索引与坐标系中的坐标(x,y)相吻合。再接着以图片对应的每一个(x,y)坐标进行一定角度的旋转得到(x1,y1),这时候的坐标x1,y1有的可能为负值,这样的负值在矩阵的索引中是不科学的。所以我们需要将负值变为正值,这相当于平移到正值的象限,平移后的坐标为(x2,y2)。最后将原图像矩阵根据索引(x,y)得到像素值,并将其赋值给相对应的矩阵索引(x2,y2)。这样就完成了。
图文并茂才能更好的理解,下面看图:
在这里插入图片描述

实现难点

下面我们就来说旋转矩阵是怎么来的以及它是什么样子的。
假设一个点为(x,y),旋转角度为 θ \theta θ。在直角坐标系中难以表示旋转,我们以极坐标系来表示: x 2 + y 2 = r 2 x = r cos ⁡ α y = r sin ⁡ α x^2+y^2 = r^2\qquad x = r\cos\alpha \qquad y = r\sin\alpha x2+y2=r2x=rcosαy=rsinα
则旋转后的坐标为: x = r cos ⁡ ( α − θ ) y = r sin ⁡ ( α − θ ) x = r\cos(\alpha-\theta) \qquad y = r\sin(\alpha-\theta) x=rcos(αθ)y=rsin(αθ)
化简后为: x 1 = x cos ⁡ θ + y sin ⁡ θ y 1 = y cos ⁡ θ − x sin ⁡ θ x1 = x\cos\theta+y\sin\theta \qquad y1 = y\cos\theta-x\sin\theta x1=xcosθ+ysinθy1=ycosθxsinθ
根据化简后的公式可得出矩阵计算公式:
[ x y ] ∗ [ cos ⁡ θ − sin ⁡ θ sin ⁡ θ cos ⁡ θ ] = [ x 1 y 1 ] \begin{bmatrix}x & y\\ \end{bmatrix}*\begin{bmatrix}\cos\theta&-\sin\theta\\ \sin\theta&\cos\theta\\ \end{bmatrix}=\begin{bmatrix}x1 & y1\\ \end{bmatrix} [xy][cosθsinθsinθcosθ]=[x1y1]
上面的旋转公式只是理论上的,但在实际运用的时候还需要对其进行进一步的改造。一般的旋转都是默认进行图像中心位置的旋转,这样我们需要先将图像中心移动到原点,在旋转,最后将其移动到原位置。(这里的w,h代表了图像的长和宽)
[ x y 1 ] ∗ [ 1 0 0 0 1 0 − w / 2 − h / 2 1 ] ∗ [ cos ⁡ θ − sin ⁡ θ 0 sin ⁡ θ cos ⁡ θ 0 0 0 1 ] ∗ [ 1 0 0 0 1 0 w / 2 h / 2 1 ] = [ x 1 y 1 1 ] \begin{bmatrix}x & y&1\\ \end{bmatrix}*\begin{bmatrix}1&0&0\\0&1&0\\-w/2&-h/2&1\\ \end{bmatrix}*\begin{bmatrix}\cos\theta&-\sin\theta&0\\ \sin\theta&\cos\theta&0\\0&0&1\\ \end{bmatrix}*\begin{bmatrix}1&0&0\\0&1&0\\w/2&h/2&1\\ \end{bmatrix}=\begin{bmatrix}x1 & y1&1\\ \end{bmatrix} [xy1] 10w/201h/2001 cosθsinθ0sinθcosθ0001 10w/201h/2001 =[x1y11]
再对其进行化简得
[ x y 1 ] ∗ [ cos ⁡ θ − sin ⁡ θ 0 sin ⁡ θ cos ⁡ θ 0 w / 2 − w / 2 ∗ cos ⁡ − h / 2 ∗ sin ⁡ w / 2 ∗ sin ⁡ − h / 2 ∗ cos ⁡ + h / 2 1 ] = [ x 1 y 1 1 ] \begin{bmatrix}x & y&1\\ \end{bmatrix}*\begin{bmatrix}\cos\theta&-\sin\theta&0\\ \sin\theta&\cos\theta&0\\w/2-w/2*\cos-h/2*\sin&w/2*\sin-h/2*\cos+h/2&1\\ \end{bmatrix}=\begin{bmatrix}x1 & y1&1\\ \end{bmatrix} [xy1] cosθsinθw/2w/2cosh/2sinsinθcosθw/2sinh/2cos+h/2001 =[x1y11]
这个就是最终我们在编程是要使用的公式
到这为止我们的核心工作就完成了,下面就可以用代码来实现了。

代码实现

在实现过程中会有一些关键点,我也都做了注释

import cv2
import numpy as np

#创建用于图像坐标转换的矩阵
def create_rotation_matrix(angle,w,h):
    cos = np.cos(np.deg2rad(angle))
    sin = np.sin(np.deg2rad(angle))
    matrix = np.array([[cos,-sin,0],[sin,cos,0],[w-w*cos-h*sin,w*sin-h*cos+h,1]])
    return matrix

#图像旋转
def rotation_image(image,matrix,background = 0):
    #获取图像的大小
    w,h,z = image.shape
    
    #创建旋转图像的矩阵的变量名
    rotation_image = None
    
    #创建图像坐标矩阵,这里新创建的矩阵相当于图纸,而旋转后的坐标代表要在纸上画图的位置
    coordinate = np.array([[i,j,1] for i in range(w) for j in range(h)],dtype = np.int16)

    #矩阵旋转
    rotation_coordinate = np.dot(coordinate,matrix)
    #将矩阵由浮点型转换为整数型,矩阵的索引需要整数。
    rotation_coordinate = rotation_coordinate.astype(np.int16)
    
    #将对应坐标像素赋值,此方法是将超出图像背景的部分进行了裁剪
    if background == 0:
        
        #创建旋转图像的矩阵
        rotation_image = np.zeros((w,h,z),dtype = np.uint8)
        
        #循环将相对应的像素进行赋值
        for i in range(coordinate.shape[0]):
            x,y,_ = coordinate[i]
            rot_x,rot_y,_ = rotation_coordinate[i]
            
            if rot_x<0 or rot_x>=w or rot_y<0 or rot_y>=h:
                #对于超出旋转矩阵范围的坐标点,不做处理
                pass
            else:
                rotation_image[rot_x,rot_y,:] = image[x,y,:]
    #非零代表不裁剪
    else:
        #找出坐标矩阵中最小的x和y
        min_coor = np.min(rotation_coordinate,axis = 0)
        
        #让坐标矩阵的所有x和y减去对应的最小x和y,这是坐标位于第一象限(都为正直)
        rotation_coordinate = rotation_coordinate-min_coor
        
        #再找出坐标矩阵的最大的x和y,作为rotation_image矩阵的大小
        max_coor = np.max(rotation_coordinate,axis = 0)

        rotation_image = np.zeros((max_coor[0]+1,max_coor[1]+1,z),dtype = np.uint8)
        
        #像素赋值
        for i in range(coordinate.shape[0]):
            x,y,_ = coordinate[i]
            rot_x,rot_y,_ = rotation_coordinate[i]
            rotation_image[rot_x,rot_y,:] = image[x,y,:]
     
    return rotation_image
        

if __name__ == '__main__':
    #读取图像
    image = cv2.imread(r'C:\Users\Administrator\Desktop\1.png')
    #image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
    w,h,_= image.shape
    
    #注意这里传入的旋转点w,h不能直接传入的是w,h = image.shape
    #传入的应该是0至(w-1)和0至(h-1)
    matrix = create_rotation_matrix(angle=-45,w =w/2,h = h/2)
    
    #background为0时代表的是旋转后的图像与原图像大小相同相当于裁剪了图像,
    #传入其他数值则为显示完整的旋转图像 
    rotation_image = rotation_image(image,matrix,background = 1)

    cv2.imshow('image',image)
    cv2.imshow('rotation_image',rotation_image)
    cv2.waitKey()
    cv2.destroyAllWindows()

效果截图:
在这里插入图片描述
左边是原图,右边是旋转之后的图像。

总结

相信大家看见了旋转之后的图像中存在小黑点,这是因为图像在旋转之后的坐标值一般都为小数,但我们将其直接转换为了整数,这会导致旋转后的位置没有像素点。可能也会是其他原因。
这种旋转方式是前向的像素传值,还有一种是后向的像素传值(按照旋转之后的坐标到原图像中寻找对应的像素值)加上一种像素的插值法就可以解决上面的问题了。
这篇文章主要是想讲一下矩阵的运用,对于解决上面的旋转之后的小黑点问题,等到下一篇文章再讲解吧。

猜你喜欢

转载自blog.csdn.net/m0_59151709/article/details/130921820