python实现2D图像线性插值旋转和双线性插值旋转

1. 旋转原理

1)坐标平移
图像旋转一般都是围绕图像的中心进行旋转,但是图像是一个矩阵,它的原点是在左上角,所以我们得先将坐标平移到中心。如图所示。
原中心点为O1,需要平移到O2,平移后的O1坐标相对于O2应为(x1- W/2, -y1+H/2)
在这里插入图片描述
坐标平移计算方式:
设原图中的像素点为[X0,Y0,1] ,图像的宽为W,高为H,则变换后的坐标为[X1,Y1,1],计算公式为:
在这里插入图片描述

2)旋转角度计算
在这里插入图片描述
上面的公式推导讲的还是挺清楚的,所以最终的旋转计算公式如下, 其中[X2,Y2]是旋转后的坐标
在这里插入图片描述
3)还原坐标
根据前两个公式计算后可以得到旋转后的坐标点,但是得到的是数学坐标系,所以我们还得还原为图像坐标系,即第一步的反运算过程。可以参考这篇博客https://blog.csdn.net/liyuan02/article/details/6750828
还原后的坐标[X3,Y3]计算公式为:
在这里插入图片描述
最终计算公式如下:
在这里插入图片描述

2. 前向映射

所谓前向映射就是将计算得到的坐标点传递给旋转后的图像。当然通过上述的计算公式得到的坐标点肯定会有小数,但是图像中的坐标是整数型,所以我们会将计算得到的浮点型数转换为小数,这样会丢失很多图像的信息,导致图像图像会有很多空洞。代码如下。

注:1.我是从图像的高开始遍历图像,所以计算公式与上面的有点不同
2.我希望旋转后的图像大小是保持不变的,超出边界的部分则会丢弃。如果你希望旋转后的图像大小保持不变,则要重新计算旋转后图像的大小。newH = Hcosθ + Wsinθ,newW = Hsinθ + Wcosθ。因此我的代码中并没有重新计算图像大小。

#前向映射
def forward_rotate(imgArray):
    H, W, channel = imgArray.shape

    pi = math.pi

    theta = random.randint(0,360)  #得到随机旋转的角度

    angle = theta * pi / 180

    matrix1 = np.array([[1, 0, 0],
                        [0, -1, 0],
                        [-0.5 * H, 0.5 * W, 1]])  #先高后宽

    matrix2 = np.array([[math.cos(angle), -math.sin(angle), 0],
                       [math.sin(angle), math.cos(angle), 0],
                       [0, 0, 1]])


    matrix3 = np.array([[1, 0, 0],
                       [0, -1, 0],
                       [0.5 * H, 0.5 * W, 1]])

    new_data = np.zeros_like(imgArray, dtype=np.uint8)

    for i in range(H):
        for j in range(W):

            dot1 = np.matmul(np.array([i, j, 1]), matrix1)  #坐标平移
            dot2 = np.matmul(dot1, matrix2)                 #坐标旋转
            dot3 = np.matmul(dot2, matrix3)                 #坐标还原

            new_coordinate = dot3

            new_i = int(math.floor(new_coordinate[0]))     #取整
            new_j = int(math.floor(new_coordinate[1]))
            
            #判断是否超出边界或者坐标是否小于1,则丢失
            if new_j>=W or new_i >=H or new_i<1 or new_j<1: 
                continue

            new_data[new_i, new_j, :] = imgArray[i, j, :]

    return new_data

图像旋转效果如下:坐左边是原图,右边是旋转后的图,可以看出旋转后的图比较模糊,因为丢失了很多像素信息。
在这里插入图片描述

3. 反向映射

所谓反向映射就是指从旋转后的图像出发,找到原图中对应的像素点,然后将原图中的像素点值传递进来。这样旋转后的图像的每个像素肯定可以对应到原图像中的一个点,然后通过插值算法使得图像更清晰完整。下面是反向映射的过程
在这里插入图片描述
1)最邻近插值
通过以下代码你可以发现和上面的前向映射几乎一模一样,唯一不同的是在最后一行,这样就很容易理解什么是前向映射和反向映射了。

#最邻近旋转
def nearest_rotate(imgArray):
    H, W, channel = imgArray.shape

    pi = math.pi

    theta = random.randint(0,360)

    angle = theta * pi / 180


    matrix1 = np.array([[1, 0, 0],
                        [0, -1, 0],
                        [-0.5 * H, 0.5 * W, 1]])

    matrix2 = np.array([[math.cos(angle), -math.sin(angle), 0],
                       [math.sin(angle), math.cos(angle), 0],
                       [0, 0, 1]])


    matrix3 = np.array([[1, 0, 0],
                       [0, -1, 0],
                       [0.5 * H, 0.5 * W, 1]])

    new_data = np.zeros_like(imgArray, dtype=np.uint8)

    for i in range(H):
        for j in range(W):

            dot1 = np.matmul(np.array([i, j, 1]), matrix1)
            dot2 = np.matmul(dot1, matrix2)
            dot3 = np.matmul(dot2, matrix3)

            new_coordinate = dot3

            new_i = int(math.floor(new_coordinate[0]))
            new_j = int(math.floor(new_coordinate[1]))

            if new_j>=W or new_i >=H or new_i<1 or new_j<1:
                continue

            new_data[i, j, :] = imgArray[new_i, new_j, :]

    return new_data

2)双线性插值
使用最近邻插值图像任然会有点模糊,所以采用双线性插值效果会好很多。这些插值算法可以参考这篇博客https://blog.csdn.net/weixin_42795611/article/details/111406043

#双线性旋转
def bilinear_rotate(imgArray):
    H, W, channel = imgArray.shape

    pi = math.pi
    theta = random.randint(0,360)
    angle = theta * pi / 180

    matrix1 = np.array([[1, 0, 0],
                        [0, -1, 0],
                        [-0.5 * H, 0.5 * W, 1]])

    matrix2 = np.array([[math.cos(angle), -math.sin(angle), 0],
                        [math.sin(angle), math.cos(angle), 0],
                        [0, 0, 1]])

    matrix3 = np.array([[1, 0, 0],
                        [0, -1, 0],
                        [0.5 * H, 0.5 * W, 1]])

    new_data = np.zeros_like(imgArray,dtype=np.uint8)

    for i in range(H):
        for j in range(W):

            dot1 = np.matmul(np.array([i, j, 1]), matrix1)
            dot2 = np.matmul(dot1, matrix2)
            dot3 = np.matmul(dot2, matrix3)

            new_coordinate = dot3

            new_i = int(math.floor(new_coordinate[0]))
            new_j = int(math.floor(new_coordinate[1]))

            u = new_coordinate[0] - new_i
            v = new_coordinate[1] - new_j

            if new_j>=W or new_i >=H or new_i<1 or new_j<1 or (i+1)>=H or (j+1)>=W:
                continue

            if (new_i + 1)>=H or (new_j+1)>=W:
                new_data[i, j, :] = imgArray[new_i,new_j, :]

            else:
                new_data[i, j, :] = (1-u)*(1-v)*imgArray[new_i,new_j, :] + \
                                   (1-u)*v*imgArray[new_i,new_j+1, :] + \
                                   u*(1-v)*imgArray[new_i+1,new_j, :] +\
                                   u*v*imgArray[new_i+1,new_j+1, :]

    return new_data

图像旋转效果如下所示,左边为原图,中间为最邻近插值,右边为双线性插值。可以看出双线性插值要清晰很多。

在这里插入图片描述
完整代码可查看python实现2D图像旋转插值算法

  • 11
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花生米生花@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值