You do not需要对图像进行 IDCT 才能无损旋转它(请注意,光栅图像的无损旋转仅适用于 90 度倍数的角度)。
以下步骤在 DCT 域中实现图像的转置:
- 转置每个 DCT 块的元素
- 转置每个 DCT 块的位置
我假设您已经可以执行以下操作:
- 从 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);
}
这是显示的图像:
这里没有发生任何奇特的事情——这只是原始图像。
现在,这是实现的代码both我上面提到的转置步骤:
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
}
这是生成的图像:
您可以看到图像现在已转置。为了实现正确的旋转,您需要结合反射与转置。
EDIT
抱歉,我忘记了反射也不是微不足道的。它也包含两个步骤:
- 显然,反映了每个DCT块在所需轴上的位置
- 不太明显的是,反转(乘以-1)每个奇数行OR列于eachDCT 块。如果垂直翻转,则反转奇数rows。如果水平翻转,则反转奇数columns.
下面的代码在转置后执行垂直反射。
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);
}
这是您得到的图像:
您会注意到,这相当于逆时针旋转 90 度。