Интерполация чрез запълване на пространството на Фурие

Наскоро се опитах да внедря в matlab прост пример за метод на интерполация, използвайки нулева подложка в домейна на Фурие. Но не мога да накарам тази работа правилно, винаги имам малко изместване на честотата, което едва ли се вижда в пространството на Фурие, но това генерира огромна грешка във времевото пространство.

Тъй като zéro padding в пространството на Фурие изглежда често срещан (и бърз) метод за интерполация, предполагам, че има нещо, което пропускам:

Ето кода на matlab:

clc;
clear all;
close all;


Fe = 3250;
Te = 1/Fe;
Nech = 100;

F1 = 500;
F2 = 1000;
FMax = 1500;

time = [0:Te:(Nech-1)*Te];
timeDiscrete = [1:1:Nech];
frequency = (timeDiscrete/Nech)*Fe;

signal = cos(2*pi*F1*(time))+cos(2*pi*F2*(time))+cos(2*pi*FMax*(time));

%Compute the FFT
spectrum=zeros(1,Nech);
for k = timeDiscrete
    for l = timeDiscrete
        spectrum(k) = spectrum(k) + signal(l)*exp(-2*pi*j*l*k/Nech);
    end
end

%Compute de inverse FFT
reconstruction=zeros(1,Nech);
for k = timeDiscrete
    for l = timeDiscrete
        reconstruction(k) = reconstruction(k) + spectrum(l)*exp(2*pi*j*l*k/Nech);
    end
end
reconstruction=reconstruction/Nech;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%    Now interpolation will take place   %%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Finterp = 6*Fe;
Tinterp = 1/Finterp;
TimeInterp = [0:Tinterp:(Nech-1)*Te];
[m,n] = size(TimeInterp);
NechInterp = n;
TimeInterpDiscrete = [1:1:NechInterp];

%Compute original signal value without any interpolation
signalResampled = cos(2*pi*F1*(TimeInterp))+cos(2*pi*F2*(TimeInterp))+cos(2*pi*FMax*(TimeInterp));

%Compute original signal interpolation by padding the fft and performing
%inverse fft on the result
semipaddedsize=floor(NechInterp/2);
padded_spectrum0 = zeros(1,semipaddedsize);
padded_spectrum0 = padarray(spectrum(1:Nech/2),[0 semipaddedsize-(Nech/2)],0,'post');
padded_spectrum = zeros(1,NechInterp);
padded_spectrum(1:semipaddedsize) = padded_spectrum0;
padded_spectrum(semipaddedsize+1:NechInterp-1) = conj(fliplr(padded_spectrum0));
% padded_spectrum = padarray(spectrum,[0 NechInterp-Nech],0,'post');
padded_timeDiscrete = [1:1:NechInterp];
padded_reconstruction = zeros(1,NechInterp);


for k = padded_timeDiscrete
    for l = padded_timeDiscrete
        padded_reconstruction(k) = padded_reconstruction(k) + padded_spectrum(l)*exp(2*pi*j*l*k/NechInterp);
    end
end
padded_reconstruction=padded_reconstruction/(1*Nech);


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%       Let's print out the result       %%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

spectrumresampled=zeros(1,NechInterp);
for k = TimeInterpDiscrete
    for l = TimeInterpDiscrete
        spectrumresampled(k) = spectrumresampled(k) + signalResampled(l)*exp(-2*pi*j*l*k/NechInterp);
    end
end

figure(2);
plot(abs(spectrumresampled)/6,'g');
hold on;
plot(abs(padded_spectrum),'b');

figure(3);

% Ground truth : deterministic signal is recomputed
plot(TimeInterp,signalResampled,'g');
hold on;
% linear interpolation between subsampled points (matlab tracing tool)
plot(time,(reconstruction),'c');
hold on;
% Padding the spectrum and reconstructing it 
plot(TimeInterp,real(padded_reconstruction),'m');
hold on;

xlabel('Time in s','FontSize',16)
ylabel('Signal value (no unit)','FontSize',16)
title('\it{ Various signal reconstruction from fourier transform }','FontSize',16)
legend('True signal', 'Reconstruction with linear interpolation', 'Reconstruction with padded spectrum');

Не мога да публикувам изображения на резултата поради моята репутация, но графиката се генерира лесно чрез matlab. Наистина бих се радвал на коментар или върху този код, или върху fft с нулево уплътнение за интерполация като цяло.

Благодаря ви предварително


person Tobbey    schedule 19.08.2013    source източник
comment
знаете ли, че единственият fft не е представителен за мощностния спектър на вашия сигнал?   -  person fpe    schedule 20.08.2013
comment
Всъщност аз не използвам FFT тук, използвам ръчно написан DFT. Знам, че мога перфектно да възстановя оригиналния си сигнал от изчисления dft, проблемът възниква, когато модифицирам спектъра, за да получа по-добра разделителна способност във времето и пространството. Доколкото ми е известно, много приложения използват fft и ifft за извършване на бърза и точна интерполация и никога не съм чувал за теоретично ограничение на трансформацията на Фурие или нейните реализации, които биха причинили проблеми? Какво имаш предвид?   -  person Tobbey    schedule 20.08.2013
comment
Тоби, мисля, че има нещо с твоя DFT. Изпълнение само на този първи блок от код и след това извършване на: plot signal; задръжте се; plot(abs(reconstruction),'g'); показва ми, че не се подреждат и че единият е обърнат наоколо и с различна величина...   -  person Frederick    schedule 21.08.2013
comment
Не обръщайте внимание на последния коментар. След като не успях да разбера защо не е наред, разбрах, че коремът обръща всичко към положителната ос. plot(real(reconstructed)) наистина показва правилното нещо. plot(imag(reconstructed)) показва малък (трябва да е нула) въображаем компонент.   -  person Frederick    schedule 21.08.2013


Отговори (3)


Благодаря ви много и на двама ви за тези съвети, реших да отговоря на собствения си въпрос, защото нямаше достатъчно място в полето за коментари:

@Try hard Наистина дефинирах грешен дискретен времеви вектор, както @Frederick също посочи, имах проблем с подпълването на моя вектор, благодаря ви, че ми дадохте правилния „matlab начин“ да го направя, не трябваше да се страхувам толкова на fftshift/ifftshift, в допълнение, използването на padarray с 'both' също би свършило работата, както беше споменато от @Frederick.

Свиването на for цикъла също беше съществена стъпка за правилното внедряване на matlab, която не използвам за целите на обучението, за да улесня моето разбиране и обвързана проверка.

Допълнителна много интересна точка @Try hard, спомената в първото изречение и която не разбрах на първо място, е фактът, че нулевото уплътняване е просто еквивалент на свиване на моите данни във времева област чрез функция sinc.

Всъщност мисля, че това е буквално еквивалентно на конволюция с функция sinc с псевдоним, наричана още дирихле ядро, чиито ограничения, когато честотата на вземане на проби нараства към безкрайност, е класическата sinc функция (вижте http://www.dsprelated.com/dspbooks/sasp/Rectangular_Window_I.html#sec:rectwinintro)

Не съм публикувал целия код тук, но целта на оригиналната ми програма беше да сравня формулата за навиване на ядрото на Дирихле, която демонстрирах в различна рамка (теоретична демонстрация, използваща дискретни изрази от серия на Фурие), sinc навивка Интерполационна формула на Whittaker–Shannon и нулево подпълване, така че трябва да бъдат дадени с много подобен резултат.

На въпроса за аподизация, мисля, че истинският отговор е, че ако сигналът ви е с ограничен обхват, нямате нужда от друга функция за аподизация освен правоъгълен прозорец.

Ако вашият сигнал не е с ограничена честотна лента или псевдоним по отношение на честотата на дискретизация, ще трябва да намалите приноса на част от спектъра с псевдоним, което се прави чрез филтрирането им с честотен филтър = аподизиращ прозорец в честотната област, който се превръща в специфични интерполационни ядра във времева област.

person Tobbey    schedule 21.08.2013
comment
Добра точка по въпроса за аподизацията, имам много да науча по този въпрос. - person Buck Thorn; 21.08.2013

ДОБРЕ. Един проблем беше начинът, по който правите IDFT за padded_reconstruction. Начинът, по който дефинирахте TimeInterp и по този начин NechInterp направиха елементите на сложния показател неправилни. Това обяснява неправилните честоти.

След това имаше проблем с включването на средната точка в домейна на Фурие (т. 50) два пъти. Беше близо до нулата, така че не създаваше много очевиден проблем, но трябваше да се включи само веднъж. Пренаписах този раздел, защото ми беше трудно да го преработя точно по начина, по който го направихте вие. Въпреки това го държах много близо. Ако правех това, щях да използвам fftshift и след това padarray(...,'both'), което би спестило работата от поставянето на нули в средата. Ако правите това за обучение и се опитвате да не използвате инструменти на matlab (напр. fftshift), тогава няма значение.

Също така преработих начина, по който определяте времето, но за да бъда честен, мисля, че може да работи по вашия начин.

Посочих промените с %‹‹‹‹‹‹‹‹‹‹

Fe = 3250;
Te = 1/Fe;
Nech = 100;

F1 = 500;
F2 = 1000;
FMax = 1500;

time = [Te:Te:(Nech)*Te];  %<<<<<<<<<<
timeDiscrete = [1:1:Nech];
frequency = (timeDiscrete/Nech)*Fe;

signal = cos(2*pi*F1*(time))+cos(2*pi*F2*(time))+cos(2*pi*FMax*(time));

%Compute the FFT
spectrum=zeros(1,Nech);
for k = timeDiscrete
    for l = timeDiscrete
        spectrum(k) = spectrum(k) + signal(l)*exp(-2*pi*j*l*k/Nech);
    end
end

%Compute de inverse FFT
reconstruction=zeros(1,Nech);
for k = timeDiscrete
    for l = timeDiscrete
        reconstruction(k) = reconstruction(k) + spectrum(l)*exp(2*pi*j*l*k/Nech);
    end
end
reconstruction=reconstruction/Nech;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%    Now interpolation will take place   %%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Finterp = 6*Fe;
Tinterp = 1/Finterp;
TimeInterp = [Tinterp:Tinterp:(Nech*6)*Tinterp];  %<<<<<<<<<<
[m,n] = size(TimeInterp);
NechInterp = n;
TimeInterpDiscrete = [1:1:NechInterp];

%Compute original signal value without any interpolation
signalResampled = cos(2*pi*F1*(TimeInterp))+cos(2*pi*F2*(TimeInterp))+cos(2*pi*FMax*(TimeInterp));

%Compute original signal interpolation by padding the fft and performing
%inverse fft on the result
padded_spectrum = zeros(1,NechInterp); %<<<<<<<<<<
padded_spectrum(1:floor(Nech/2-1)) = spectrum(1:floor(Nech/2-1)); %<<<<<<<<<<
padded_spectrum(end-floor(Nech/2)+1:end) = spectrum(floor(Nech/2)+1:end); %<<<<<<<<<<

padded_reconstruction = zeros(1,NechInterp);
for k = TimeInterpDiscrete %<<<<<<<<<<(no reason for new variable)
    for l = TimeInterpDiscrete %<<<<<<<<<<(no reason for new variable)
        padded_reconstruction(k) = padded_reconstruction(k) + padded_spectrum(l)*exp(2*pi*j*l*k/NechInterp);
    end
end
padded_reconstruction=padded_reconstruction/(1*Nech);
person Frederick    schedule 20.08.2013
comment
Имайте предвид, че начинът, по който дефинирате timeDiscrete, е несъвместим с дефиницията на DFT (както в оригиналната публикация) - person Buck Thorn; 21.08.2013
comment
Бих гласувал за вашия отговор, тъй като ми помогна, но репутацията ми не е достатъчно висока. Използването на padarray с 'both' и fftshift и ifftshift е добра идея. - person Tobbey; 21.08.2013
comment
@TryHard timeDiscrete е итератор. Не може да започне от 0, защото променливите на matlab започват от 1. Може би обаче трябва да бъде (l-1)*(k-1) в степента. - person Frederick; 21.08.2013
comment
Прав си за частта с итератора, както и за втората точка. Първо и преди всичко изразът за DFT трябва да е правилен, кодът трябва да бъде написан съответно. - person Buck Thorn; 21.08.2013

Резултатът, който наблюдавате във времевата област, е звънене поради конволюция на sinc функция с вашите оригинални данни. Това е еквивалентът във времевата област на умножение с правоъгълен прозорец в честотната област, което всъщност е това, което правите, когато запълвате нула. Не забравяйте да apodize!

Публикувам повторно вашия код след свиване на циклите (което значително ускорява изчислението), предефиниране на обхватите на променливите за време и честота (вижте дефиницията на DFT, за да видите защо), и премахване на една от операциите за подплънки, които изпълнявате, което честно казано не разбрах смисъла.

clc;
clear all;
close all;

Fe = 3250;
Te = 1/Fe;
Nech = 100;
mlt = 10;
F1 = 50; 
F2 = 100; 
FMax = 150; 

time = [0:Te:(Nech-1)*Te];
%timeDiscrete = [1:1:Nech];
timeDiscrete = [0:1:Nech-1];
frequency = (timeDiscrete/Nech)*Fe;

signal = cos(2*pi*F1*(time))+cos(2*pi*F2*(time))+cos(2*pi*FMax*(time));
spectrum =  signal*exp(-2*pi*j*timeDiscrete'*timeDiscrete/Nech);
fspec = [0:Nech-1]*Fe/Nech;
reconstruction = spectrum*exp(2*pi*j*timeDiscrete'*timeDiscrete/Nech)/Nech;

figure
plot(time,signal)
hold on
plot(time,reconstruction,'g:')

% **** interpolation ****

Finterp = 6*Fe;
Tinterp = 1/Finterp;
TimeInterp = [0:Tinterp:(Nech-1)*Te];
NechInterp = length(TimeInterp);
%TimeInterpDiscrete = [1:NechInterp];
TimeInterpDiscrete = [0:NechInterp-1];

%Compute original signal value without any interpolation
signalResampled = cos(2*pi*F1*(TimeInterp))+cos(2*pi*F2*(TimeInterp))+cos(2*pi*FMax*(TimeInterp));

%Compute original signal interpolation by padding the fft and performing
%inverse fft on the result
padded_spectrum0 = spectrum;
padded_spectrum0(NechInterp) = 0;
fspecPadded = [0:NechInterp-1]*Finterp/NechInterp;

padded_reconstruction = padded_spectrum0*exp(2*pi*j*TimeInterpDiscrete'*TimeInterpDiscrete/NechInterp)/(1*Nech);
spectrumresampled = signalResampled*exp(-2*pi*j*TimeInterpDiscrete'*TimeInterpDiscrete/NechInterp);
fresampled = [0:NechInterp-1]*Fe/NechInterp;

% **** print out ****

figure(2);
hold on;
plot(fspec,abs(spectrum),'c');
plot(fresampled,abs(spectrumresampled)/6,'g--');
plot(fspecPadded,abs(padded_spectrum0),'m:');
xlabel('Frequency in Hz','FontSize',16)
ylabel('Signal value (no unit)','FontSize',16)
legend('True signal', 'Reconstruction with resampled spectrum', 'Padded spectrum');

figure(3);
% Ground truth : deterministic signal is recomputed
plot(TimeInterp,signalResampled,'g');
hold on;
% Padding the spectrum and reconstructing it 
plot(TimeInterp,real(padded_reconstruction),'m:');
xlabel('Time in s','FontSize',16)
ylabel('Signal value (no unit)','FontSize',16)
title('\it{ Various signal reconstruction from fourier transform }','FontSize',16)
legend('True signal', 'Reconstruction with padded spectrum');

И ето изображение на ужасно изкривен сигнал поради подложка в честотната област:

въведете описание на изображението тук

Възможно е известно подобрение, като първо се приложи fftshift за центриране на спектъра и подложка на алтернативните страни на центрирания спектър, след което се обърне операцията fftshift:

Nz = NechInterp-Nech;
padded_spectrum0 = ifftshift([ zeros(1,floor(Nz/2)) fftshift(spectrum) zeros(1,floor(Nz/2)+rem(Nz,2)) ]); % replaces (NechInterp) = 0;
fspecPadded = [0:NechInterp-1]*Finterp/NechInterp;

След това стигате до този много по-добър интерполиран сигнал във времева област, тъй като операцията за подплънка не води до такива резки спадове в спектъра (все пак може да са възможни някои подобрения с по-нататъшно бърникане):

въведете описание на изображението тук

person Buck Thorn    schedule 20.08.2013