65.9K
CodeProject 正在变化。 阅读更多。
Home

通过剪切旋转GIF

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2024年6月28日

CPOL

3分钟阅读

viewsIcon

10733

通过剪切方法旋转 GIF 图像。

引言

旋转图像是一个常见需求,特别是带有透明度的 GIF。然而,不能使用旋转矩阵,因为它需要对像素进行插值,这在颜色索引图像中是不可能的。但是,可以使用剪切方法进行旋转。

背景

要旋转一个点,我们必须对 x 和 y 分量应用正弦和余弦。具体来说

x' = x cos(theta) - y sin(theta)

y' = x sin(theta) + y cos(theta)

因此,可以通过迭代目标像素、应用此方程式的逆运算(theta = -theta)并找到源图像中的正确点来旋转图像。但是,样本点恰好击中像素的可能性极低。因此,我们必须对四个最接近的像素进行插值,以获得输出像素。如果我们不这样做,并选择最接近的像素,我们将得到奇怪的伪影,因为源像素被加倍和丢弃,这在技术上称为“混叠”。

对于真彩色图像,插值相对简单(只要我们不要求数学上完美的结果),但对于颜色索引图像,例如 GIF 文件,这是不可能的,因为我们只有一组有限的像素可供选择。因此,使用旋转矩阵方法,我们必须接受混叠。

然而,有可能避免这种情况。通过使用另一种旋转算法。旋转等价于三个剪切变换。要将图像旋转 180 度,我们可以剪切整个宽度,剪切整个高度,然后再次剪切整个宽度。这应该很容易可视化,并让你对该算法有一个直观的理解。它也适用于其他值。

有一些附带条件。只能按整个像素旋转。因此,对于有限的图像,不允许进行小旋转。图像行和列被分成源像素的运行,这本身就是混叠的来源。但是结果相对较好,并且该方法已经确立。

代码被打包成一个可运行的程序。 GIF 图像是矩形的,并且只能在内切圆内旋转几何图形,而不会改变图像的尺寸。因此,该程序适用于带有透明度的 GIF。扩大尺寸的替代方案将很快导致图像大小失控膨胀。

使用代码

代码都是纯 ANSI C。只需键入

gcc *.c

即可获得可执行文件。

/**
  Rotate an image, using the shearing method.

Params:
  binary - the binary or colour-indexed image
  width - image width
  height - image height
  cx - centre x, y co-ordinates (pass in 1.5, 1.5 for the centre of a 3x3 image)
  cy - centre x, y co-ordinates (pass in 1.5, 1.5 for the centre of a 3x3 image)
  theta - angle to rotate by
  out[out] - buffer for output (can't be the same as input)
  
Returns: 0 for success

  Conventional image rotation by the matrix method causes destination pixels to be
    sub-samples of source pixels. This isn't a problem with continous tone images where
    the new pixel values can be obtained by interpolation. However with binary or
    colour-index images, interpolation isn't possible. The shearing method preserves the
    pixels, at some cost in rotational accuracy.

  */
int rotatebyshear(unsigned char *binary, int width, int height, double cx, double cy, double theta, unsigned char *out)
{
    double alpha;
    double beta;
    int dpx;
    int tx, ty;
    int x, y;

    assert(binary != out);
    theta = fmod(theta, 2 * PI);

    if(theta >= -PI/2 && theta <= PI/2)
    {
      alpha = -tan(theta/2);
      beta = sin(theta);

      for(y=0;y<height;y++)
      {
         dpx = (int) floor(alpha * (y - cy) + 0.5);
         for(x=0;x<width;x++)
         {
           ty = y + (int) floor(beta * (x + dpx - cx) + 0.5);
           tx = x + dpx + (int) floor(alpha * (ty - cy) + 0.5);
           if(tx >= 0 && tx < width && ty >= 0 && ty < height)
             out[y*width+x] = binary[ty*width+tx];
           else
             out[y*width+x] = 0;
         }
      }
    }
    else
    {
        alpha = -tan( (theta + PI) / 2);
        beta = sin(theta + PI);
        for(y=0;y<height;y++)
        {
         dpx = (int) floor(alpha * (y - cy) + 0.5);
         for(x=0;x<width;x++)
         {
           ty = y + (int) floor(beta * (x + dpx - cx) + 0.5);
           tx = x + dpx + (int) floor(alpha * (ty - cy) + 0.5);
           tx = (int) (cx-(tx - cx));
           ty = (int) (cy-(ty - cy));
           if(tx >= 0 && tx < width && ty >= 0 && ty < height)
             out[y*width+x] = binary[ty*width+tx];
           else
             out[y*width+x] = 0;
         }
      }
    }

    return 0;
}

这就是你想要的功能。

剪切变换由矩阵给出

1 sx
0 1

并且是最基本的简单变换之一。

 

结果

Lena 256 GIF 旋转后的 Lena
Lena 256 colours Lena rotated

结果并不完美。你会看到 Lena 的帽子和左脸颊上出现严重的“锯齿”,当然图像边缘本身也出现了混叠。但是保留了所有像素。在连续色调区域中没有带状或质量下降。

关注点

你可能想知道我们是如何获得如此高质量的 256 色 Lena,她的肩膀上根本没有任何机械带状的。当然,我们使用了主成分分析算法。

历史

该代码是 github 上二进制图像库的一部分

https://github.com/MalcolmMcLean/binaryimagelibrary

© . All rights reserved.