Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Быстрая свертка! Как?
Форум разработчиков электроники ELECTRONIX.ru > Цифровая обработка сигналов - ЦОС (DSP) > Алгоритмы ЦОС (DSP)
coolibin
Нужен алгоритм быстрой свертки(желательно подробный). Если кто то опишет или даст линк, я буду только благодарен smile.gif
DRUID3
Цитата(coolibin @ Oct 10 2007, 09:42) *
Нужен алгоритм быстрой свертки(желательно подробный). Если кто то опишет или даст линк, я буду только благодарен smile.gif

Свертка во-временнОй области соответствует умножению спектров базиса Фурье в частотной. Спектры быстро вычисляюЦЦо через FFT. Вот Вам и метод.

Собственно и "гугл" рулит: http://alglib.sources.ru/fft/fastconvolution.php
fontp
Цитата(DRUID3 @ Oct 10 2007, 13:12) *
Свертка во-временнОй области соответствует умножению спектров базиса Фурье в частотной. Спектры быстро вычисляюЦЦо через FFT. Вот Вам и метод.

Собственно и "гугл" рулит: http://alglib.sources.ru/fft/fastconvolution.php


И здесь есть очень симпатичная книга по быстрым преобразованиям
и соответствующие реализации свёртки хоть через Фурье, хоть через Хартли
http://www.jjj.de/fxt/fxtpage.html
coolibin
Цитата
И здесь есть очень симпатичная книга по быстрым преобразованиями соответствующие реализации свёртки хоть через Фурье, хоть через Хартлиhttp://www.jjj.de/fxt/fxtpage.html

ссылка битая, помоему

а если честно, то когда береш 100 000 точек сигнала и сворачиваешь с 8 000 точками коэф. фильтра, то всё равно приходится не мало ждать. Тем более результат после быстрой свертки хуже(может не правильно сворачивал)


алгоритм быстрой свертки брал здесь - http://graphics.cs.msu.ru/courses/cg02b/li...y/dspcourse.pdf
fontp
Цитата(coolibin @ Oct 11 2007, 12:23) *
ссылка битая, помоему

а если честно, то когда береш 100 000 точек сигнала и сворачиваешь с 8 000 точками коэф. фильтра, то всё равно приходится не мало ждать. Тем более результат после быстрой свертки хуже(может не правильно сворачивал)
алгоритм быстрой свертки брал здесь - http://graphics.cs.msu.ru/courses/cg02b/li...y/dspcourse.pdf


Ссылка не битая. По Хартли немного быстрее, чем по Фурье, если данные не комплесные. Ну а чудес не бывает
DRUID3
Цитата(coolibin @ Oct 11 2007, 11:23) *
ссылка битая, помоему

а если честно, то когда береш 100 000 точек сигнала и сворачиваешь с 8 000 точками коэф. фильтра, то всё равно приходится не мало ждать. Тем более результат после быстрой свертки хуже(может не правильно сворачивал)
алгоритм быстрой свертки брал здесь - http://graphics.cs.msu.ru/courses/cg02b/li...y/dspcourse.pdf

ссылка не битая, просто надо посидеть и разобраЦЦо. А немало ждать на какой машине??? Теоретически результат свертки и быстрой свертки абсолютно идентичны. Интересно, с какими типами данных работаете?
Oldring
Цитата(coolibin @ Oct 10 2007, 10:42) *
Нужен алгоритм быстрой свертки(желательно подробный). Если кто то опишет или даст линк, я буду только благодарен smile.gif


Свертки бывают разные. Алгоритмы тоже.
Теория довольно подробно изложеная в Блейхут "Быстрые алгоритмы цифровой обработки сигналов@/
Если я не ошибаюсь, когда-то была на dsp-book
coolibin
Цитата
ссылка не битая, просто надо посидеть и разобраЦЦо. А немало ждать на какой машине??? Теоретически результат свертки и быстрой свертки абсолютно идентичны. Интересно, с какими типами данных работаете?

по ссылке зашел. машина - pentium 4 2.8. данные - double
coolibin
Что в этой свертке неправильно?
Код
void fast_conv(const double *pSigA, unsigned int uSizeA, const double *pSigB,unsigned int uSizeB, double *pSigR){
    complex<double>    *pSigACx, *pSigBCx;
    size_t i;

    uint uConvSize = get_length(uSizeA + uSizeB - 1);
    
    pSigACx = new complex<double>[uConvSize];
    pSigBCx = new complex<double>[uConvSize];

    for(i = 0; i != uSizeA; i++){
        pSigACx[i] = complex<double>(pSigA[i], 0.0);
    }

    for(i = uSizeA; i != uConvSize; i++){
        pSigACx[i] = complex<double>(0.0, 0.0);
    }

    fft(pSigACx, uConvSize, false);

    for(i = 0; i != uSizeB; i++){
        pSigBCx[i] = complex<double>(pSigB[i], 0.0);
    }

    for(i = uSizeB; i != uConvSize; i++){
        pSigBCx[i] = complex<double>(0.0, 0.0);
    }

    fft(pSigBCx, uConvSize, false);

    for(i = 0; i != uConvSize; i++){
        pSigACx[i] *= pSigBCx[i];
    }

    fft(pSigACx, uConvSize, true);

    for(i = 0; i != uSizeA; i++){
        pSigR[i] = pSigACx[i].real();
    }

    delete [] pSigACx;
    delete [] pSigBCx;
}


fft работает правильно!
shasik
Цитата(coolibin @ Oct 12 2007, 14:39) *
Что в этой свертке неправильно?


Уверены в правильности вычисления прямого и обратного(!) преобразования Фурье?

Цитата(coolibin @ Oct 11 2007, 11:23) *
когда береш 100 000 точек сигнала и сворачиваешь с 8 000 точками коэф. фильтра, то всё равно приходится не мало ждать. Тем более результат после быстрой свертки хуже(может не правильно сворачивал)


Вы уверены, что оно Вам надо? Пример: отсчеты сигнала приходят один раз в секунду. Фильтр длиной 8000. Т.е. когда будет получен 8000-й отсчет можно посчитать свертку и выплюнуть результат. Считать можно в лоб (всего то 8000). Если же применять fft в том виде, как это сделали Вы, то придется ждать пока не будет получен весь(!) сигнал и выполнять fft для сигналов длиной 100000(!). Опеределитесь, что Вам нужно!
А еще есть алгоритмы разбиения свертки длинного сигнала на несколько более коротких - секционирование называется. Обратите внимание на это.
coolibin
Цитата(shasik @ Oct 12 2007, 15:13) *
Уверены в правильности вычисления прямого и обратного(!) преобразования Фурье?

При пошаговой проверке спектры вычисляются правильно. Могу показать код

Цитата(shasik @ Oct 12 2007, 15:13) *
Вы уверены, что оно Вам надо? Пример: отсчеты сигнала приходят один раз в секунду. Фильтр длиной 8000. Т.е. когда будет получен 8000-й отсчет можно посчитать свертку и выплюнуть результат. Считать можно в лоб (всего то 8000). Если же применять fft в том виде, как это сделали Вы, то придется ждать пока не будет получен весь(!) сигнал и выполнять fft для сигналов длиной 100000(!). Опеределитесь, что Вам нужно!
А еще есть алгоритмы разбиения свертки длинного сигнала на несколько более коротких - секционирование называется. Обратите внимание на это.


К тому времени когда запускается моя программа сигнал уже получен. Я работаю с уже с известными данными.
Grt
При вычислении свертки, нужно обязательно учитывать предысторию сигнала.
Длина предыстории определяется порядком фильтра, т.е. если вы берете порядок фильтра 8. Значит 8 семплов сигнала вам нужно брать с предыдущего фрейма.
coolibin
Цитата(Grt @ Oct 12 2007, 17:13) *
При вычислении свертки, нужно обязательно учитывать предысторию сигнала.
Длина предыстории определяется порядком фильтра, т.е. если вы берете порядок фильтра 8. Значит 8 семплов сигнала вам нужно брать с предыдущего фрейма.


С этим проблем нет

В статье http://alglib.sources.ru/fft/fastconvolution.php вводятся понятия положительной и отрицательной длинны отклика. В моем случае сворачивается сигнал и коэфф. фильтра, как определить длину положительного/отрицательного отклика. Пополам чтоли?
shasik
Почему
Код
uint uConvSize = get_length(uSizeA + uSizeB - 1);

а не просто
Код
uint uConvSize = uSizeA + uSizeB - 1;
rloc
Цитата(coolibin @ Oct 10 2007, 10:42) *
Нужен алгоритм быстрой свертки(желательно подробный). Если кто то опишет или даст линк, я буду только благодарен smile.gif

Цитата(DRUID3 @ Oct 10 2007, 13:12) *
Свертка во-временнОй области соответствует умножению спектров базиса Фурье в частотной. Спектры быстро вычисляюЦЦо через FFT. Вот Вам и метод.

Позвольте поинтересоваться, а какой длительности сигнал нужно сворачивать? Может со сжатием в частотной области поспешили? Точно знаю, что фирма Intel в своих библиотеках MKL перед сверткой определяет длительность и в зависимости от этого уже сворачивает. Исходные данные даны не полностью, а задачку уже решаете.
coolibin
Цитата(shasik @ Oct 15 2007, 13:08) *
Почему
Код
uint uConvSize = get_length(uSizeA + uSizeB - 1);

а не просто
Код
uint uConvSize = uSizeA + uSizeB - 1;


Функция get_length ищет ближайшую степень двойки которая больше чем заданое число
shasik
В Matlab'e приведенный выше пример полностью прокатывает, поэтому еще раз обращаю внимание на вычисление прямого и обратного БПФ.
А еще смущает строка:
Код
for(i = 0; i != uSizeA; i++)


Если будете делать фильтрацию в лоб и с помощью БПФ, а затем посмотрите графики выходных отфильтрованных сигналов, то у Вас может наблюдаться небольшой сдвиг (в зависимости от того, как Вы поступили с начальными условиями - считали, что до этого сигнал был равен 0 либо подождали, пока не прибудет необходимое количество отсчетов). И если для сравнения результатов Вы вычисляли СКО сигналов - результаты будут не совпадать
coolibin
Алгоритм описаный здесь http://alglib.sources.ru/fft/fastconvolution.php, т .е. отклик делится на две части полож. и отриц., работаем прекрасно и на моем БПФ. Значит не в БПФ дело
shasik
Цитата(coolibin @ Oct 12 2007, 14:39) *
Что в этой свертке неправильно?
Код
void fast_conv(const double *pSigA, unsigned int uSizeA, const double *pSigB,unsigned int uSizeB, double *pSigR){
    complex<double>    *pSigACx, *pSigBCx;

          .......................................................................

    delete [] pSigACx;
    delete [] pSigBCx;
}


fft работает правильно!

Ну, тогда ладно, уговорили! Сдаемся! Говорите отгадку - что неправильно?

ЗЫ: расшифруйте понятие "неправильно" (архивчик с тестовым сигналом; с тем, что должно было получиться; с тем, что получилось на сам деле и с отсчетами фильтра очень помог бы).
coolibin
Цитата(shasik @ Oct 18 2007, 11:05) *
Ну, тогда ладно, уговорили! Сдаемся! Говорите отгадку - что неправильно?

ЗЫ: расшифруйте понятие "неправильно" (архивчик с тестовым сигналом; с тем, что должно было получиться; с тем, что получилось на сам деле и с отсчетами фильтра очень помог бы).

Я не знаю что неправильно. Дело в том, что метод разделения отклика на положительный и отрицательный работает и на моем БПФ и мне этого достаточно.
Вот рабочая свертка:
Код
//быстрая свертка
void COOLibin_math::fastconvolution(std::vector<double>& Signal,
                                const std::vector<double>& Responce,
                                uint uPositiveLen)
{
    std::vector<std::complex<double> >    SignalCx, ResponceCx;
    uint uConvLen, uSignalLen, uNegativeLen;
    size_t i;

    uSignalLen = Signal.size();
    uNegativeLen = Responce.size() - uPositiveLen - 1;

    uConvLen = uSignalLen;
    if(uNegativeLen > uPositiveLen ){
        uConvLen += uNegativeLen;
    }
    else{
        uConvLen += uPositiveLen;
    }

    if( uNegativeLen + uPositiveLen + 1 > uConvLen ){
        uConvLen = uNegativeLen + uPositiveLen + 1;
    }

    i = 1;
    while(i < uConvLen)
    {
        i = i*2;
    }
    uConvLen = i;

    SignalCx.resize(uConvLen);
    ResponceCx.resize(uConvLen);

    for(i = 0; i != uSignalLen; i++){
        SignalCx[i] = std::complex<double>(Signal[i], 0.0);
    }

    for(i = uSignalLen; i != uConvLen; i++){
        SignalCx[i] = std::complex<double>(0.0, 0.0);
    }

    for(i = 0; i != uConvLen; i++){
        ResponceCx[i] = std::complex<double>(0.0, 0.0);
    }

    for(i = 0; i <= uPositiveLen; i++){
        ResponceCx[i] = std::complex<double>(Responce[i + uNegativeLen], 0.0);
    }

    for(i = 1; i <= uNegativeLen; i++){
        ResponceCx[uConvLen - i] = std::complex<double>(Responce[uNegativeLen - i], 0.0);
    }

    fft(SignalCx, false);
    fft(ResponceCx, false);

    for(i = 0; i != uConvLen; i++){
        SignalCx[i] *= ResponceCx[i];
    }

    fft(SignalCx, true);

    for(i = 0; i != uSignalLen; i++){
        Signal[i] = SignalCx[i].real();
    }
}


А это сам БПФ:
Код
void COOLibin_math::rec_fft(std::vector<std::complex<double> >& Signal,
                                        bool bInv){

    uint uSignalLen = Signal.size();
    if(uSignalLen == 1){
        return;
    }

    const double    PI = 3.1415926535897932;
    std::vector<std::complex<double> >    Odd(uSignalLen/2), Even(uSignalLen/2);

    for(size_t i = 0; i != uSignalLen/2; i++){
            Odd[i] = Signal[2*i];
            Even[i] = Signal[2*i + 1];
    }

    rec_fft(Odd, bInv);
    rec_fft(Even, bInv);

    double fSign;
    if(!bInv){
        fSign = 1.0;
    }
    else{
        fSign = -1.0;
    }

    std::complex<double> W(1.0, 0.0), WStep(cos(2.0*PI/uSignalLen), sin(fSign*2.0*PI/uSignalLen)), Temp;

    for(size_t k = 0; k != uSignalLen/2; k++){
        Temp = W*Even[k];
        Signal[k] = Odd[k] + Temp;
        Signal[k + uSignalLen/2] = Odd[k] - Temp;
        W *= WStep;
    }
}



void COOLibin_math::fft(std::vector<std::complex<double> >& Signal,
                                        bool bInv){

    rec_fft(Signal, bInv);

    if(bInv){
         uint uSignalLen = Signal.size();
         for(std::vector<std::complex<double> >::iterator it = Signal.begin(); it != Signal.end(); it++){
               (*it) /= uSignalLen;
         }
     }
}
shasik
Цитата(coolibin @ Oct 16 2007, 09:36) *
Алгоритм описаный здесь http://alglib.sources.ru/fft/fastconvolution.php


Оно Вам надо?
Я гораздо больше уважаю Алгоритмы для Программеров. Там можно скачать книгу (на 3 МБ в архиве) и сишные исходники (около 1 МБ). Почитав главу относящуюся к преобразованию Фурье и его применение, Вы многое поймете. Например, поймете, что зря использовали для своего алгоритма название fft, т.к. он далеко не быстрый. Может быть поэтому у Вас долго считает.

А на счет разделения на + и - части - реально с этим никто не заморачивается, все делается гороздо проще. Сходите по ссылке, рекомендую
coolibin
А никто не скажет, когда применять быструю свертку, а когда обычную? или всегда выгодно быструю?
shasik
Цитата(coolibin @ Oct 23 2007, 10:03) *
А никто не скажет, когда применять быструю свертку, а когда обычную? или всегда выгодно быструю?

Все зависит: от длины используемого фильтра (скользящее среднее чаще всего делается напрямую без использования быстрых алгоритмов), от используемого железа (многие процессоры ЦОС поддерживают выполнение свертки на аппаратном уровне причем параллельно с основным железом), от требования к быстродействию программы, от наличия времени на написание программы (свертка в лоб всегда легче пишется), от того доступен ли весь сигнал сразу или накопление его отсчетов идет в real-time да и расположение свезд на небе тоже наверное влияет каким-нибудь образом.

А если серьезно то в первом приближении оценивается по количеству операций - на малых длинах быстрые алгоритмы дают совсем незначительный выйгрыш по быстродействию, а иногда даже медленнее решения в лоб, а гемороя от них много.

ЗЫ: Еще раз говорю: скачайте то, что я рекомендовал! Там есть, что нужно.
coolibin
Цитата(shasik @ Oct 23 2007, 10:26) *
ЗЫ: Еще раз говорю: скачайте то, что я рекомендовал! Там есть, что нужно.


А что именно? там много всякой всячины!

Книга fxtbook.pdf что ли?
fontp
Цитата(coolibin @ Oct 23 2007, 12:00) *
А что именно? там много всякой всячины!

Книга fxtbook.pdf что ли?


Всё что описано в книге - реализовано в fxt package на C
http://www.jjj.de/fxt/fxtpage.html#fxt
shasik
Цитата(coolibin @ Oct 23 2007, 11:00) *
А что именно? там много всякой всячины!
Книга fxtbook.pdf что ли?

Ну, Вы и ленивый!
Книга в формате pdf в архиве - http://www.jjj.de/fxt/fxtbook.pdf.gz
Книга в формате dvi в архиве - http://www.jjj.de/fxt/fxtbook.dvi.gz - самый небольшой размер
Исходники (также в архиве) - http://www.jjj.de/fxt/fxt-2007.10.14.tgz

Ксати, специально посмотрел соответствующий раздел - Ваша мечта сбылась, там тоже есть разделение на +/- часть.
ЗЫ: надеюсь переведете с английского уже без посторонней помощи
Ole2
Подскажите пожалуйста, откуда берется выигрыш при проведении свертки или корреляции в частотной области? Количество умножений и суммирований одинаковое в частотной и временной областях. Помимо этого для частотной области добавляется прямое и обратное преобразование Фурье. Получается проигрыш. Гмм...
fontp
QUOTE (Ole2 @ Dec 3 2010, 18:54) *
Подскажите пожалуйста, откуда берется выигрыш при проведении свертки или корреляции в частотной области? Количество умножений и суммирований одинаковое в частотной и временной областях. Помимо этого для частотной области добавляется прямое и обратное преобразование Фурье. Получается проигрыш. Гмм...


Гмм...Где ж одинаковое? В частотной области свертка (N*K операций) превращается просто в произведение спектров (N).
Ole2
Ну ладно…
Пусть мы имеем отсчеты квадратурного (комплексного) сигнала, поступающие непрерывно на коррелятор, имеющий линию задержки длинной k с комплексными эталонными коэффициентами. Один такт вычислений в этом случае даст 4 операции умножения и 3 суммирования на 1 ячейку линии задержки (комплексное умножение). Так как этих ячеек k, то общее количество операций k*(4умн+3сум). Кроме того, необходио суммирование результата умножения по всем ячейкам. Итого количество операций Ot для корреляция во времени:
Ot= k*(4умн+3сум)+kсум;
Для частотной области только перемножаем спектры сигналов и получаем количество опеаций k*(4умн+3сум). Суммировать здесь действительно не надо. Но для выполнения БПФ необходимо k*log2(k) операций. Итого для вычисления свертки в частотной области необходимо Of операций:
Of= k*(4умн+3сум)+2*k*log2(k).
Так при k=2048 для вычисления свертки во времени необходимо 16384 операций умножения и суммирования, а для частоной 59392. Чудовищный проигрыш…
fontp
QUOTE (Ole2 @ Dec 4 2010, 21:54) *
Ну ладно…
Пусть мы имеем отсчеты квадратурного (комплексного) сигнала, поступающие непрерывно на коррелятор, имеющий линию задержки длинной k с комплексными эталонными коэффициентами. Один такт вычислений в этом случае даст 4 операции умножения и 3 суммирования на 1 ячейку линии задержки (комплексное умножение). Так как этих ячеек k, то общее количество операций k*(4умн+3сум). Кроме того, необходио суммирование результата умножения по всем ячейкам. Итого количество операций Ot для корреляция во времени:
Ot= k*(4умн+3сум)+kсум;
Для частотной области только перемножаем спектры сигналов и получаем количество опеаций k*(4умн+3сум). Суммировать здесь действительно не надо. Но для выполнения БПФ необходимо k*log2(k) операций. Итого для вычисления свертки в частотной области необходимо Of операций:
Of= k*(4умн+3сум)+2*k*log2(k).
Так при k=2048 для вычисления свертки во времени необходимо 16384 операций умножения и суммирования, а для частоной 59392. Чудовищный проигрыш…


Ерунда... Свертка это сумма x(i)*x(i+k) длинною N, для всех к от 0 до K. Всего N*K операций, порядка N*N в ассимптотике, K<N
2 фурье это порядка 2*N*log(N) плюс N умножений спектров для всех K сразу
Вот и сравните N*log(N) и N*N при больших N

Прямая свертка при больших N может быть предпочтительней только если число сдвигов К мало
:-)
http://www.williamspublishing.com/PDF/5-8459-0710-1/part.pdf

см. страницу 327, таблицу 5.1.
Ole2
Если делать БПФ поблочно, то конечно, выигрыш будет. Я просто решил, что БПФ необходимо после каждого приходящего отсчета. Спасибо за разъяснения, а то в литературе это описано не явно, а я привык работать в реальном масштабе после каждой единичной выборки сигнала.

Ссылка http://www.williamspublishing.com/PDF/5-8459-0710-1/part.pdf это книга Айфичера. Забыл туда заглянуть.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.