Как повернуть изображение в частотной области? - PullRequest
5 голосов
/ 13 февраля 2011

Я слышал, что должно быть возможно сделать вращение без потерь на изображении JPEG. Это означает, что вы делаете вращение в частотной области без IDCT. Я пытался гуглить, но ничего не нашел. Может ли кто-нибудь осветить это?

То, что я имею в виду без потерь, это то, что я не теряю никакой дополнительной информации в ротации. И, конечно, это возможно только при повороте на 90 градусов.

Ответы [ 2 ]

6 голосов
/ 13 февраля 2011

Вам не нужно IDCT изображение, чтобы повернуть его без потерь (обратите внимание, что вращение без потерь для растровых изображений возможно только для углов, кратных 90 градусам).

Следующие шаги позволяют выполнить транспонирование изображения в области DCT:

  1. транспонировать элементы каждого блока DCT
  2. транспонировать позиции каждого блока DCT

Я предполагаю, что вы уже можете делать следующее:

  • Получение необработанных коэффициентов DCT из изображения JPEG (если нет, см. здесь )
  • Запишите коэффициенты обратно в файл (если вы хотите сохранить повернутое изображение)

Я не могу показать вам полный код, потому что он довольно сложный, но вот фрагмент, где я 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

Вы можете видеть, что изображение теперь транспонировано. Чтобы добиться правильного вращения, вам нужно объединить отражение с транспозицией.

EDIT

Извините, я забыл, что отражение тоже не тривиально. Он также состоит из двух этапов:

  1. Очевидно, отразите положения каждого блока DCT на требуемой оси
  2. Менее очевидно, инвертировать (умножить на -1) каждую нечетную строку ИЛИ столбец в каждый блок DCT. Если вы переворачиваете вертикально, инвертируйте нечетные строки . Если вы переворачиваете по горизонтали, инвертируйте нечетные столбцы .

Вот код, который выполняет вертикальное отражение после транспонирования.

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 градусов против часовой стрелки.

0 голосов
/ 13 февраля 2011

Отказ от ответственности: : -)

Правда, я знаю алгоритм сжатия JPEG только на очень поверхностном уровне. То, что я знаю, взято из слегка устаревшей, но превосходной книги Рика Бута Inner Loops , глава 17: JPEG.

У меня нет полного ответа на ваш вопрос, скорее, у меня есть смутное представление о том, каким может быть решение. Может быть, это уже будет полезно для вас. Честно говоря, я был бы несколько удивлен, увидев, что я правильно понял.


Поворот изображения в формате JPEG без потерь представляется возможным только в том случае, если вам не нужно будет сначала декодировать его с использованием IDCT, а затем перекодировать его снова после поворота изображения, поскольку это два вычислительных шага, когда потеря информации может случается.

Это кажется возможным вообще, потому что изображение, закодированное в формате JPEG, уже находится в частотной области, поскольку DCT (дискретное косинусное преобразование) уже выполнено для него. Позвольте мне привести один короткий отрывок из вышеприведенной книги (стр. 325):

Обычно упоминается как DCT [& hellip;]. Концептуально, то, что происходит, это то, что 8 & times; 8 часть изображения умножается на два других в 8 раз; 8 матриц для получения производной 8 & раз; 8 матрица. [& Hellip;]

Обычно два 8 раз; 8 умножение матриц потребует 1,204 (64 & times; 8 & times; 2) шагов умножения. Часть волшебства DCT заключается в том, что очень специальные матрицы, выбранные для этого шага, имеют много внутренних симметрий , поэтому существует способ выполнить математику всего с 80 шагами умножения. Именно эта симметрия спасает день для JPEG и сохраняет алгоритм довольно быстрым. & mdash; (выделено мной.)

Я мог бы представить, что симметрии в матрицах преобразования DCT позволяют позже вращать преобразованные 8 раз; 8 матриц под некоторыми очень специфическими углами без перцептивного изменения изображения (кроме того факта, что оно вращается, конечно). На концептуальном уровне я имею в виду следующее: допустим, вы изменили оригинал 8 раз; 8 пиксельных блоков с использованием таких матриц, как:

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

(я использую символы вместо фактических числовых значений, поскольку я хочу показать только симметрию этой матрицы.)

Такая матрица преобразования может позволять вам поворачивать преобразованные матрицы на кратное 90 градусов в любом направлении, поскольку сама матрица преобразования всегда будет выглядеть одинаково, если преобразована под одинаковыми углами.

Если это действительно то, о чем вы читали, это означало бы, что вращение без потерь не будет работать для произвольных углов поворота. Углы, которые гарантируют отсутствие потерь, будут зависеть от матриц, используемых во время кодирования JPEG.

...