通过剪切的任意角度旋转(Paeth算法)

Sil*_*key 7 java algorithm image-processing rotation

我正在尝试编写Alan Paeth描述的3剪切旋转算法的Java实现.问题不在于计算值,而是将旋转点拟合到图像网格上.在本文中,通过以下计算给出的3个连续剪刀进行旋转:

  1. x = x + alpha*y
  2. y = y + beta*x
  3. x = x + alpha*y

Alpha和beta由给定角度(θ;以弧度表示)通过以下公式计算:

  • beta = sin(theta)
  • alpha = - tan(theta/2)

使用这些公式,点围绕坐标系的中心旋转.

为了校正负值,我将相应轴的最小计算坐标添加到每个点,以使最小值始终为0.

到目前为止我的Java实现:

ShiftPoint[] val = new ShiftPoint[m*n];
double minX = 0,minY = 0, maxX = 0, maxY = 0;
double alpha = -1d*  Math.tan(Math.toRadians(theta)/2d);
double beta = Math.sin(Math.toRadians(theta));
for(int a = 0; a < m; a++) {
    for(int b = 0; b < n; b++) {
        ShiftPoint temp = new ShiftPoint(a, b, values[a][b]);
        double newX = b + alpha * a;    //first shear
        double newY = a + beta * newX;  //second shear
        newX += alpha * newY;           //third shear
        temp.setX(newX);
        temp.setY(newY);
        val[m * b + b] = temp;
    }
}
Run Code Online (Sandbox Code Playgroud)

注意: ShiftPoint是一个简单的自编写类,用于保存矩阵内的特定坐标和值(在图像处理的情况下:像素的rgb值).这是计算的图形表示: 在此输入图像描述

问题: 虽然计算值似乎是正确的并且图形表示显示旋转实际有效,但我不确定如何将计算值拟合到固定网格上的图像(或2d数组)没有扭曲它.另外我还不完全理解Paeths论文中给出的实现(对于x轴剪切):

在此输入图像描述

我得到的是,skewi是计算值的整数部分,而skewf是小数部分,但是宽度,高度,颜色和左边应该是什么?另外:为什么他在y值上加0.5并且在第一次计算时没有考虑x值?

注意:我知道Java提供了简单的旋转图像的方法,但我正在尝试实现这个特定的算法,只是为了它的乐趣.我也知道可以通过websearch找到的3-5个网站(例如#1#2)并尝试解释该算法,但首先他们不使用java,其次他们主要参考Paeth的示例实现,所以它们并不十分有用.

rwp*_*rwp 1

这种图像旋转方法背后的基本原理有两个:

  • 定义坐标变换,将旋转图像中的 x 和 y 轴旋转到原始图像的 x 和 y 轴上
  • 通过在原始图像中对应点附近的值之间进行插值来计算旋转图像中的每个像素

第一步通常比较容易,涉及 x 和 y 坐标的简单线性组合。剪切变换(严格来说不是旋转,因为它们不保留每个像素的面积)只涉及 x -> x + alpha * y 之类的内容。

Paeth 的论文(可追溯至 1986 年)中给出的算法似乎是一种经过精心优化的方法,用于执行剪切变换的第二步(插值)。我认为这可以归结为沿 x 轴的分段线性插值,但以一种不需要对输出图像中的每个像素进行多次数组查找的形式编写。更清晰(且效率稍低)的方法可能涉及 skewf * Pixel(x-skewi-1, y) + (1-skewf) * Pixel(x-skewi, y) 之类的方法。

这种特殊的算法显然是高度专门用于单轴倾斜的。对于一般旋转,您可能需要更像是对每个 2x2 正方形像素进行双线性插值,这些像素围绕与旋转图像中每个像素的中心相对应的未旋转位置。(计算这个中心像素值可能是 Paeth 代码中 y+0.5 的起源。)

考虑到我们现在相对于 1986 年拥有多少计算能力,我怀疑如果您为这种双线性插值包含一个显式公式,您的代码会更容易理解,即使它确实比 Paeth 的方法使用更多的数组查找,除非您确实需要最大性能。