如何在频域中旋转图像?

时间:2011-02-13 09:36:05

标签: image-processing image-manipulation jpeg image-rotation

我听说应该可以在jpeg图像上进行无损旋转。这意味着您在没有IDCT的情况下在频域中进行旋转。我试过谷歌但没找到任何东西。有人可以为此带来一些启示吗?

无意义的意思是我不会在轮换中丢失任何其他信息。当然,这可能只有在旋转90度的倍数时才有可能。

2 个答案:

答案 0 :(得分:6)

需要IDCT图像无损旋转(请注意,光栅图像的无损旋转仅适用于90度倍数的角度。)

以下步骤在DCT域中实现图像的转置:

  1. 转置每个DCT块的元素
  2. 转置每个DCT块的位置
  3. 我假设您已经可以执行以下操作:

    • 从JPEG图像中抓取原始DCT系数(如果没有,请参阅here
    • 将系数写回文件(如果要保存旋转的图像)

    我无法向您展示完整的代码,因为它非常复杂,但这里是IDCT图像的位置(注意IDCT仅用于显示目的):

    Size s = coeff.size();
    Mat result = cv::Mat::zeros(s.height, s.width, CV_8UC1);
    
    for (int i = 0; i < s.height - DCTSIZE + 1; i += DCTSIZE)
    for (int j = 0; j < s.width  - DCTSIZE + 1; j += DCTSIZE)
    {
        Rect rect = Rect(j, i, DCTSIZE, DCTSIZE);
        Mat dct_block = cv::Mat::Mat(coeff, rect);
        idct_step(dct_block, i/DCTSIZE, j/DCTSIZE, result);
    }
    

    这是显示的图像:

    Lenna

    这里没有任何幻想 - 这只是原始图像。

    现在,这是实现两者我上面提到的转置步骤的代码:

    Size s = coeff.size();
    Mat result = cv::Mat::zeros(s.height, s.width, CV_8UC1);
    
    for (int i = 0; i < s.height - DCTSIZE + 1; i += DCTSIZE)
    for (int j = 0; j < s.width  - DCTSIZE + 1; j += DCTSIZE)
    {
        Rect rect = Rect(j, i, DCTSIZE, DCTSIZE);
        Mat dct_block = cv::Mat::Mat(coeff, rect);
        Mat dct_bt(cv::Size(DCTSIZE, DCTSIZE), coeff.type());
        cv::transpose(dct_block, dct_bt);                // First transposition
        idct_step(dct_bt, j/DCTSIZE, i/DCTSIZE, result); // Second transposition, swap i and j
    }
    

    这是结果图像:

    transposed

    您可以看到图像现在已转置。要实现正确的旋转,您需要将反射与换位相结合。

    修改

    抱歉,我忘记了反思也不是微不足道的。它还包括两个步骤:

    1. 显然,反映每个DCT块在所需轴上的位置
    2. 不太明显,在每个 DCT块中反转(乘以-1)每个奇数行 OR 列。如果您正在垂直翻转,请反转奇数。如果您正在水平翻转,请反转奇数
    3. 这是在转置后执行垂直反射的代码。

      for (int i = 0; i < s.height - DCTSIZE + 1; i += DCTSIZE)
      for (int j = 0; j < s.width  - DCTSIZE + 1; j += DCTSIZE)
      {
          Rect rect = Rect(j, i, DCTSIZE, DCTSIZE);
          Mat dct_block = cv::Mat::Mat(coeff, rect);
      
          Mat dct_bt(cv::Size(DCTSIZE, DCTSIZE), coeff.type());
          cv::transpose(dct_block, dct_bt);
      
          // This is the less obvious part of the reflection.
          Mat dct_flip = dct_bt.clone();
          for (int k = 1; k < DCTSIZE; k += 2)
          for (int l = 0; l < DCTSIZE; ++l)
              dct_flip.at<double>(k, l) *= -1;
      
          // This is the more obvious part of the reflection.
          idct_step(dct_flip, (s.width - j - DCTSIZE)/DCTSIZE, i/DCTSIZE, result);
      }
      

      以下是您获得的图片:

      final

      您会注意到这会逆时针旋转90度。

答案 1 :(得分:0)

  

免责声明:: - )

     

不可否认,我只知道JPEG压缩算法非常肤浅。我所知道的是Rick Booth稍微过时但非常出色的书Inner Loops,第17章:JPEG。

     

我对你的问题没有完整的答案,而是我对解决方案可能是什么有一个模糊的概念。也许这对你有帮助。老实说,看到我说得对,我会感到有些惊讶。


JPEG图像的无损旋转似乎只有在您不必首先使用IDCT对其进行解码,然后在旋转图像后再次对其进行重新编码时才有可能,因为这是信息丢失可能导致的两个计算步骤发生。

这看起来很可行,因为编码为JPEG的图像已经在频域中,因为已经对其执行了DCT(离散余弦变换)。让我引用上面一本书的短文(第325页):

  

通常被称为DCT [...]。从概念上讲,发生的是图像的8×8片段乘以另外两个8×8矩阵以产生导数8×8矩阵。 [...]

     

通常,两个8×8矩阵乘法需要1,204(64×8×2)个乘法步骤。 DCT的部分神奇之处在于为此步骤选择的非常特殊的矩阵具有许多内部对称性,因此有一种方法只需80个乘法步骤即可执行数学运算。正是这种对称性为JPEG节省了一天,并使算法保持相当快。 - (我强调了这一点。)

我可以想象,DCT变换矩阵中的对称性使得以后可以在某些非常特定的角度旋转变换的8×8矩阵而不会在感知上改变图像(当然,除了它旋转的事实)。在概念层面,我的意思是这样的:假设您使用如下矩阵转换了原始的8×8像素块:

                * . . . . . . *
                . * . . . . * .
                . . * . . * . .
                . . . * * . . .
                . . . * * . . .
                . . * . . * . .
                . * . . . . * .
                * . . . . . . *

(我使用的是符号而不是实际的数值,因为我只想显示这个矩阵的对称性。)

这样的变换矩阵可能允许您在任何方向上将变换后的矩阵旋转90度的倍数,因为如果以相同的角度变换,变换矩阵本身看起来总是相同的。

如果这确实是你所读到的,那就意味着无损旋转对任意旋转角度都不起作用。保证无损失的角度取决于JPEG编码期间使用的矩阵。