huidong

首页 | 会员登录 | 关于争取 2022 寒假做出汇东网 Ver3.0.0 !
搜索文章


之前在 2.5D Racing Car 项目里面用到的一个算法,算是简易的图像旋转算法吧


当时我没想出这个算法,后来期末考试期间的一个晚自习想出来了,哈哈。其实不难,我只是后知后觉罢了。


不过之前忘记把这个算法单独发出来,现在补一下,代码如下。

// 2D 坐标旋转
POINT Rotate2D(int x, int y, double radian)
{
    if (radian == 0)    return { x,y };
    return { (int)(x * cos(radian) - y * sin(radian)),
        (int)(x * sin(radian) + y * cos(radian)) };
}

// 获取一图像坐标在旋转后的新坐标位置(自适应长宽式旋转)
// width, height 原图像宽高
// x, y 为旋转点原坐标
// radian 旋转弧度
POINT GetRotatedImagePos(int width, int height, int x, int y, double radian)
{
    int left = 0, button = 0;
    POINT points[3] = { {width,0},{0,height},{width,height} };

    // 由于 IMAGE 对象的坐标系 y 轴向下,通用坐标系 y 轴向上,
    // 故旋转时需要以相反角度旋转才等效于在 IMAGE 对象的坐标系中正常旋转
    for (int j = 0; j < 3; j++)
    {
        points[j] = Rotate2D(points[j].x, points[j].y, -radian);
    }
    POINT p = Rotate2D(x, y, -radian);

    // 记录最左和最下的端点
    for (int j = 0; j < 3; j++)
    {
        if (points[j].x < points[left].x)
        {
            left = j;
        }
        if (points[j].y < points[button].y)
        {
            button = j;
        }
    }

    // 若图像旋转后端点坐标越界,将其补回正常位置
    if (points[left].x < 0)        p.x += -points[left].x;
    if (points[button].y < 0)    p.y += -points[button].y;

    return p;
}


其实和普通的 2D 旋转都差不多,但是多了一道平移的工序,因为 IMAGE 对象旋转完了要自适应长宽嘛,这也正是唯一要注意的地方。


说原理之前,先贴个实现相同功能的乐色算法吧……

// 获取小车在旋转后的地图中的位置
// NOTE: 旧方法,耗时 60ms 左右,不推荐使用。
POINT GetRotatedCarPosition(int width, int height, int x, int y, double radian)
{
    IMAGE p(width, height), p2;
    int nw, nh;
    SetWorkingImage(&p);
    fillrectangle(x, y, x + 2, y + 2);
    rotateimage(&p2, &p, radian, BLACK, true, false);
    nw = p2.getwidth();
    nh = p2.getheight();
    SetWorkingImage();
    DWORD* buf = GetImageBuffer(&p2);
    for (int i = 0; i < nw * nh; i++)
    {
        if (buf[i] == WHITE)
        {
            return { i - (i / nw) * nw,i / nw };
        }
    }

    // 寻找失败
    return { -1,-1 };
}


这个就是直接再开了一张新的 IMAGE 对象做相同的旋转,然后在图上找点来取坐标,非常的 low。

一开始我就写的是这个,当时周末没太多时间搞出算法来,实属无奈之举。


浅谈原理,其实不难。


首先要明确的是:IMAGE 对象的坐标系中 y 轴是向下的,就意味着图片中的一个点旋转多少度,在平面直角坐标系中进行计算的时候,要旋转相反的角度,这样一来,这个坐标放回到 IMAGE 对象坐标系中,新坐标才是正常的。


关于 2D 旋转这里就不说了。


其次,就是自适应图像大小的问题。其实也很简单了,画个图看看


这是 IMAGE 对象的坐标系:

——————————————> x

|                   |

|                   |

|  这是图像   |

|                   |

|____________|

|

y


然后你假设它绕原点,逆时针,旋转个 60 度吧?那是不是图像有一部分到 x 轴上面去了?

这个时候为了实现图像自适应,就要把图像往下平移吧?把 x 轴上面多出来的一块,给他正好平移到与 x 轴相切的位置,即 y=0 处。


然后你再想,要是转角更大一点,270 度?请想象一下,这个时候有一部分图像到 y 轴左侧了吧?同样是给他平移,向右平移回来,平移到 x=0 处。


这样图像大小自适应就好了。


那么这一步操作放到平面直角坐标系的旋转当中,请想一想。

因为这两个坐标系 y 轴方向相反嘛,逆时针旋转的 60 度,现在应该变成顺时针了,原先在 IMAGE 坐标系里面的 “x 轴上面多出来的一块”就变成 x 轴下面多出来的一块了,对吧?

那继续旋转,由于 x 轴方向还是一样的,继续旋转的话就会超出到 y 轴左侧,和在 IMAGE 坐标系中是一样的。


所以总结一下:


  1. 一个点在 IMAGE 对象的坐标系中旋转一定角度,就是在平面直角坐标系中旋转相反的角度。

  2. 在平面直角坐标系中,一个点旋转完毕后,需要判定这个点所属的整个图像,是否超出第一象限(除非旋转角度是 360° 的倍数,否则都会超出的,大家都懂),这个时候就要把超出的部分平移,直到整个图像回到第一象限,此时被旋转的点的坐标就被平移了,得到最终的旋转坐标。


在代码中实现的时候,2D 旋转就不用说了,平移的那部分的实现方法,我是把那张图像的 除了在原点外的 其余三个顶点也跟着进行了旋转,然后得到三个顶点中的 x 坐标最小值,y 坐标最小值,如果这个值是负数,那么就把 旋转后的点的坐标(x 或 y 坐标)相应地加上这个值的相反数,就完成了图像大小的自适应。


是不是很简单~~






返回首页


Copyright (C) 2018-2024 huidong