(1) |
откуда следует, что
(2) |
Таким образом, диапазон степеней (ik) с помощью формулы (1) мы сокращаем с
Пусть n – длина исходной последовательности чисел {x0, x1, …, xn-1} чётна, тогда преобразование Фурье для такой последовательности можно записать в виде:
(3) |
С учётом формулы (1), а также выражения
формулу (3) часто записывают в виде:
(4) |
Рассмотрим первую сумму формулы (3). Положив n / 2 = m, получим:
1) | n = 2m; |
2) | ; |
3) | . |
Аналогичные рассуждения можно провести и в отношении второй суммы формулы (3). Таким образом, ДПФ исходной последовательности размерностью n=2m свелось к двум ДПФ последовательностей размерностью m чисел каждая, составленных из чётных и нечётных элементов исходной последовательности.
Формулы (3) и (4) являются основой для построения алгоритма быстрого преобразования Фурье (БПФ). Этот алгоритм за счёт рекурсивного применения формулы (3) сводит ДПФ исходной последовательности размерности n=2l, где l є N, к набору 2-точечных преобразований Фурье.
По определению имеем:
Умножим x-k на Wn-jk и просуммируем по k = 0 .. (n-1):
Можно доказать, что
где a є Z, a>0, поэтому
имеем:
– мы получили формулу для обратного ДПФ (ОДПФ):
откуда видно, что ОДПФ есть прямое ДПФ (ПДПФ) результата предыдущего ПДПФ, делённое на n.
Двумерное ДПФ – это ДПФ над матрицей:
где k,i = (0, n-1), l,j = (0, m-1), то есть это есть последовательное одномерное ДПФ сначала над строками, а потом над строками.
Общий вид обратного двумерного ДПФ:
При практической реализации в качестве исходных данных для демонстрации работы алгоритмов ДПФ удобно использовать значения точек изображения, подвергающегося обработке другими алгоритмами. При этом под «значениями» точек понимается либо значения каждого из цветовых каналов, либо значение яркости (найденное по известной формуле), либо значение типа TColor элемента массива Pixels[], – это неважно. Важно то, что всё выполняется примерно по следующий схеме:
Пример процедуры для выполнения одномерного БПФ приведён ниже:
// (C) 1999 Иван Головинов procedure LinearFDFT (Re, Im: PReal; Count: Word; Direct: ShortInt); var I: Word; K: Real; PIm, PRe: PReal; procedure LFDFT (SrcRe, SrcIm: PReal; Cnt: Word); var EvenRe, OddRe, PEvenRe, POddRe, PRe, PSrcRe: PReal; …; // То же - для мнимых Factor: Real; HalfCnt, I, Size: Word; HEvenIm, HEvenRe, HOddIm, HOddRe: THandle; X, Y, WIm, WRe: Real; begin PSrcRe := SrcRe; …; if Cnt = 2 then begin // Тривиальная операция «бабочка» Inc (PSrcRe); …; X := SrcRe^; Y := PSrcRe^; SrcRe^ := X + Y; PSrcRe^ := X - Y; X := SrcIm^; Y := PSrcIm^; SrcIm^ := X + Y; PSrcIm^ := X - Y; end else begin // Переупорядочение и рекурсивный вызов для полпоследовательностей Factor := K / Cnt; HalfCnt := Cnt div 2; Size := HalfCnt * SizeOfReal + 1; HEvenRe := GlobalAlloc (GMem_Fixed, Size); …; HOddRe := GlobalAlloc (GMem_Fixed, Size); …; EvenRe := GlobalLock (HEvenRe); …; OddRe := GlobalLock (HOddRe); …; PEvenRe := EvenRe; …; POddRe := OddRe; …; // Разбивка на (не)чётные подпоследовательности for I := 0 to Cnt - 1 do begin if Odd (I) then begin POddRe^ := PSrcRe^; …; Inc (POddRe); …; end else begin PEvenRe^ := PSrcRe^; …; Inc (PEvenRe); …; end; Inc (PSrcRe); …; end; // Рекурсивная обработка (не)чётных подпоследовательностей LFDFT (EvenRe, EvenIm, HalfCnt); LFDFT (OddRe, OddIm, HalfCnt); // Сборка обработанных подпоследовательностей PSrcRe := SrcRe; …; PRe := SrcRe; …; Inc (pRe, HalfCnt); …; PEvenRe := EvenRe; …; POddRe := OddRe; …; for I := 0 to HalfCnt - 1 do begin WRe := Cos (Factor * I); WIm := - Sin (Factor * I); PSrcRe^ := POddRe^ * WRe - POddIm^ * WIm; PSrcIm^ := POddRe^ * WIm + POddIm^ * WRe; PRe^ := pSrcRe^; …; PSrcRe^ := pEvenRe^ + PSrcRe^; …; PRe^ := PEvenRe^ - PRe^; …; Inc (PSrcRe); …; Inc (PRe); …; Inc (PEvenRe); …; Inc (POddRe); …; end; GlobalUnlock (HEvenRe); …; GlobalUnlock (HOddRe); …; GlobalFree (HEvenRe); …; GlobalFree (HOddRe); …; end; end; begin K := 2 * Pi * Direct; LFDFT (Re, Im, Count); if Direct < 0 then begin // Дополнительное деление на n для обратного преобразования PRe := Re; …; for I := 1 to Count do begin PRe^ := PRe^ / Count; …; Inc (PRe); …; end; end; end;
Оптимизация здесь может заключаться в использовании другого типа данных (константа SizeOfReal может быть заменена на выхов оператора SizeOf(Type) для конкретного типа Type), других способов выделения и освобождения памяти (например, с использованием функций GetMem и FreeMem) и т. п. В случае двумерного преобразования используется та же функция, но последовательно, сначала для строк, потом для столбцов (или наоборот).