фазовая корреляция с использованием FFTW

Теперь для фазовой корреляции (два изображения) я использую преобразование 1d. Как использовать 2d-преобразование (это будет быстрее?), как использовать мудрость и поддержку многопоточности? если вы приведете пример кода, это будет лучше.

void phase_correlation2D( IplImage* src, IplImage *tpl, IplImage *poc )
{
    int     i, j, k;
    double  tmp;

    /* get image properties */
    int width    = src->width;
    int height   = src->height;
    int step     = src->widthStep;
    int fft_size = width * height;

    /* setup pointers to images */
    uchar   *src_data = ( uchar* ) src->imageData;
    uchar   *tpl_data = ( uchar* ) tpl->imageData;
    double  *poc_data = ( double* )poc->imageData;

    //fftw_init_threads(); // for MT
    //fftw_plan_with_nthreads(2);

    /* allocate FFTW input and output arrays */
    fftw_complex *img1 = ( fftw_complex* )fftw_malloc( sizeof( fftw_complex ) * width * height );
    fftw_complex *img2 = ( fftw_complex* )fftw_malloc( sizeof( fftw_complex ) * width * height );
    fftw_complex *res  = ( fftw_complex* )fftw_malloc( sizeof( fftw_complex ) * width * height );

    /* setup FFTW plans */
    fftw_plan fft_img1 = fftw_plan_dft_2d( width ,height, img1, img1, FFTW_FORWARD,  FFTW_ESTIMATE );
    fftw_plan fft_img2 = fftw_plan_dft_2d( width ,height, img2, img2, FFTW_FORWARD,  FFTW_ESTIMATE );
    fftw_plan ifft_res = fftw_plan_dft_2d( width ,height, res,  res,  FFTW_BACKWARD, FFTW_ESTIMATE );

    /*int f= fftw_init_threads();
    fftw_plan_with_nthreads(10);*/

    /* load images' data to FFTW input */
    for( i = 0, k = 0 ; i < height ; i++ ) {
        for( j = 0 ; j < width ; j++, k++ ) {
            img1[k][0] = ( double )src_data[i * step + j];
            img1[k][1] = 0.0;

            img2[k][0] = ( double )tpl_data[i * step + j];
            img2[k][1] = 0.0;
        }
    }

    /* obtain the FFT of img1 */
    fftw_execute( fft_img1 );

    /* obtain the FFT of img2 */
    fftw_execute( fft_img2 );

    /* obtain the cross power spectrum */
    for( i = 0; i < fft_size ; i++ ) {
        res[i][0] = ( img2[i][0] * img1[i][0] ) - ( img2[i][1] * ( -img1[i][1] ) );
        res[i][1] = ( img2[i][0] * ( -img1[i][1] ) ) + ( img2[i][1] * img1[i][0] );

        tmp = sqrt( pow( res[i][0], 2.0 ) + pow( res[i][1], 2.0 ) );

        res[i][0] /= tmp;
        res[i][1] /= tmp;
    }

    /* obtain the phase correlation array */
    fftw_execute(ifft_res);

    //normalize and copy to result image
    for( i = 0 ; i < fft_size ; i++ ) {
        poc_data[i] = res[i][0] / ( double )fft_size;
    }

    /* deallocate FFTW arrays and plans */
    //fftw_cleanup_threads();
    fftw_destroy_plan( fft_img1 );
    fftw_destroy_plan( fft_img2 );
    fftw_destroy_plan( ifft_res );
    fftw_free( img1 );
    fftw_free( img2 );
    fftw_free( res );
}

person mrgloom    schedule 03.02.2012    source источник


Ответы (2)


Наконец, вот код:

class Peak
{
public:
    CvPoint pt;
    double  maxval;
};
Peak old_opencv_FFT(IplImage* src,IplImage* temp)
{
    CvSize imgSize = cvSize(src->width, src->height);
    // Allocate floating point frames used for DFT (real, imaginary, and complex) 
    IplImage* realInput = cvCreateImage( imgSize, IPL_DEPTH_64F, 1 ); 
    IplImage* imaginaryInput = cvCreateImage( imgSize, IPL_DEPTH_64F, 1 ); 
    IplImage* complexInput = cvCreateImage( imgSize, IPL_DEPTH_64F, 2 );
    int nDFTHeight= cvGetOptimalDFTSize( imgSize.height ); 
    int nDFTWidth= cvGetOptimalDFTSize( imgSize.width ); 
    CvMat* src_DFT = cvCreateMat( nDFTHeight, nDFTWidth, CV_64FC2 ); 
    CvMat* temp_DFT = cvCreateMat( nDFTHeight, nDFTWidth, CV_64FC2 );
    CvSize dftSize = cvSize(nDFTWidth, nDFTHeight);
    IplImage* imageRe = cvCreateImage( dftSize, IPL_DEPTH_64F, 1 ); 
    IplImage* imageIm = cvCreateImage( dftSize, IPL_DEPTH_64F, 1 );
    IplImage* imageImMag = cvCreateImage( dftSize, IPL_DEPTH_64F, 1 ); 
    IplImage* imageMag = cvCreateImage( dftSize, IPL_DEPTH_64F, 1 ); 

    CvMat tmp; 
    // Processing of src 
    cvScale(src,realInput,1.0,0);
    cvZero(imaginaryInput); 
    cvMerge(realInput,imaginaryInput,NULL,NULL,complexInput);
    cvGetSubRect(src_DFT,&tmp,cvRect(0,0,src->width,src->height)); 
    cvCopy(complexInput,&tmp,NULL);
    if (src_DFT->cols>src->width)
    { 
        cvGetSubRect(src_DFT,&tmp,cvRect(src->width,0,src_DFT->cols-src->width,src->height)); 
        cvZero(&tmp); 
    } 
    cvDFT(src_DFT,src_DFT,CV_DXT_FORWARD,complexInput->height); 
    cvSplit(src_DFT,imageRe,imageIm,0,0);  

    // Processing of temp
    cvScale(temp,realInput,1.0,0); 
    cvMerge(realInput,imaginaryInput,NULL,NULL,complexInput); 
    cvGetSubRect(temp_DFT,&tmp,cvRect(0,0,temp->width,temp->height)); 
    cvCopy(complexInput,&tmp,NULL); 
    if (temp_DFT->cols>temp->width) 
    { 
        cvGetSubRect(temp_DFT,&tmp,cvRect(temp->width,0,temp_DFT->cols-temp->width,temp->height)); 
        cvZero( &tmp ); 
    } 
    cvDFT(temp_DFT,temp_DFT,CV_DXT_FORWARD,complexInput->height);

    // Multiply spectrums of the scene and the model (use CV_DXT_MUL_CONJ to get correlation instead of convolution) 
    cvMulSpectrums(src_DFT,temp_DFT,src_DFT,CV_DXT_MUL_CONJ); 

    // Split Fourier in real and imaginary parts 
    cvSplit(src_DFT,imageRe,imageIm,0,0); 

    // Compute the magnitude of the spectrum components: Mag = sqrt(Re^2 + Im^2) 
    cvPow( imageRe, imageMag, 2.0 ); 
    cvPow( imageIm, imageImMag, 2.0 ); 
    cvAdd( imageMag, imageImMag, imageMag, NULL ); 
    cvPow( imageMag, imageMag, 0.5 ); 

    // Normalize correlation (Divide real and imaginary components by magnitude) 
    cvDiv(imageRe,imageMag,imageRe,1.0); 
    cvDiv(imageIm,imageMag,imageIm,1.0); 
    cvMerge(imageRe,imageIm,NULL,NULL,src_DFT); 

    // inverse dft 
    cvDFT( src_DFT, src_DFT, CV_DXT_INVERSE_SCALE, complexInput->height ); 
    cvSplit( src_DFT, imageRe, imageIm, 0, 0 ); 

    double minval = 0.0; 
    double maxval = 0.0; 
    CvPoint minloc; 
    CvPoint maxloc; 
    cvMinMaxLoc(imageRe,&minval,&maxval,&minloc,&maxloc,NULL); 

    int x=maxloc.x; // log range 
    //if (x>(imageRe->width/2)) 
    //        x = x-imageRe->width; // positive or negative values 
    int y=maxloc.y; // angle 
    //if (y>(imageRe->height/2)) 
    //        y = y-imageRe->height; // positive or negative values 

    Peak pk;
    pk.maxval= maxval;
    pk.pt=cvPoint(x,y);
    return pk;
}
void phase_correlation2D( IplImage* src, IplImage *tpl, IplImage *poc )
{
    int     i, j, k;
    double  tmp;

    /* get image properties */
    int width    = src->width;
    int height   = src->height;
    int step     = src->widthStep;
    int fft_size = width * height;

    /* setup pointers to images */
    uchar   *src_data = ( uchar* ) src->imageData;
    uchar   *tpl_data = ( uchar* ) tpl->imageData;
    double  *poc_data = ( double* )poc->imageData;

    /* allocate FFTW input and output arrays */
    fftw_complex *img1 = ( fftw_complex* )fftw_malloc( sizeof( fftw_complex ) * width * height );
    fftw_complex *img2 = ( fftw_complex* )fftw_malloc( sizeof( fftw_complex ) * width * height );
    fftw_complex *res  = ( fftw_complex* )fftw_malloc( sizeof( fftw_complex ) * width * height );   

    /* setup FFTW plans */
    fftw_plan fft_img1 = fftw_plan_dft_2d( height ,width, img1, img1, FFTW_FORWARD,  FFTW_ESTIMATE );
    fftw_plan fft_img2 = fftw_plan_dft_2d( height ,width, img2, img2, FFTW_FORWARD,  FFTW_ESTIMATE );
    fftw_plan ifft_res = fftw_plan_dft_2d( height ,width, res,  res,  FFTW_BACKWARD, FFTW_ESTIMATE );

    /* load images' data to FFTW input */
    for( i = 0, k = 0 ; i < height ; i++ ) {
        for( j = 0 ; j < width ; j++, k++ ) {
            img1[k][0] = ( double )src_data[i * step + j];
            img1[k][1] = 0.0;

            img2[k][0] = ( double )tpl_data[i * step + j];
            img2[k][1] = 0.0;
        }
    }

    ///* Hamming window */
    //double omega = 2.0*M_PI/(fft_size-1);
    //double A= 0.54;
    //double B= 0.46;
    //for(i=0,k=0;i<height;i++)
    //{
    //    for(j=0;j<width;j++,k++)
    //    {
    //        img1[k][0]= (img1[k][0])*(A-B*cos(omega*k));
    //        img2[k][0]= (img2[k][0])*(A-B*cos(omega*k));
    //    }
    //}

    /* obtain the FFT of img1 */
    fftw_execute( fft_img1 );

    /* obtain the FFT of img2 */
    fftw_execute( fft_img2 );

    /* obtain the cross power spectrum */
    for( i = 0; i < fft_size ; i++ ) {
        res[i][0] = ( img2[i][0] * img1[i][0] ) - ( img2[i][1] * ( -img1[i][1] ) );
        res[i][1] = ( img2[i][0] * ( -img1[i][1] ) ) + ( img2[i][1] * img1[i][0] );

        tmp = sqrt( pow( res[i][0], 2.0 ) + pow( res[i][1], 2.0 ) );

        res[i][0] /= tmp;
        res[i][1] /= tmp;
    }

    /* obtain the phase correlation array */
    fftw_execute(ifft_res);

    //normalize and copy to result image
    for( i = 0 ; i < fft_size ; i++ ) {
        poc_data[i] = res[i][0] / ( double )fft_size;
    }

    /* deallocate FFTW arrays and plans */
    fftw_destroy_plan( fft_img1 );
    fftw_destroy_plan( fft_img2 );
    fftw_destroy_plan( ifft_res );
    fftw_free( img1 );
    fftw_free( img2 );
    fftw_free( res );
}

Peak FFTW_test(IplImage* src,IplImage* temp)
{
    clock_t start=clock();

    int t_w=temp->width;
    int t_h=temp->height;

    /* create a new image, to store phase correlation result */
    IplImage* poc = cvCreateImage( cvSize(temp->width,temp->height ), IPL_DEPTH_64F, 1 );

    /* get phase correlation of input images */
    phase_correlation2D( src, temp, poc );

    /* find the maximum value and its location */
    CvPoint minloc, maxloc;
    double  minval, maxval;
    cvMinMaxLoc( poc, &minval, &maxval, &minloc, &maxloc, 0 );

    /* IplImage* poc_8= cvCreateImage( cvSize(temp->width, temp->height ), 8, 1 );
    cvConvertScale(poc,poc_8,(double)255/(maxval-minval),(double)(-minval)*255/(maxval-minval));
    cvSaveImage("poc.png",poc_8); */

    cvReleaseImage( &poc );

    clock_t end=clock();

    int time= end-start;

    //fprintf( stdout, "Time= %d using clock() \n" ,time/*dt*/ );
    //fprintf( stdout, "Maxval at (%d, %d) = %2.4f\n", maxloc.x, maxloc.y, maxval );

    CvPoint pt;
    pt.x= maxloc.x;
    pt.y= maxloc.y;
    //4 variants?
    //if(maxloc.x>=0&&maxloc.x<=t_w/2&&maxloc.y>=0&&maxloc.y<=t_h/2)
    //{
    //  pt.x= src->width-maxloc.x;
    //  pt.y= -maxloc.y;
    //}
    //if(maxloc.x>=t_w/2&&maxloc.x<=t_w&&maxloc.y>=0&&maxloc.y<=t_h/2)
    //{
    //  pt.x= src->width-maxloc.x;
    //  pt.y= src->height-maxloc.y;
    //}
    //if(maxloc.x>=0&&maxloc.x<=t_w/2&&maxloc.y>=t_h/2&&maxloc.y<=t_h)
    //{
    //  /*pt.x= -maxloc.x;
    //  pt.y= -maxloc.y;*/
    //  pt.x= src->width-maxloc.x;
    //  pt.y= src->height-maxloc.y;
    //} 
    //if(maxloc.x>=t_w/2&&maxloc.x<=t_w&&maxloc.y>=t_h/2&&maxloc.y<=t_h)
    //{
    //  pt.x= -maxloc.x;
    //  pt.y= src->height-maxloc.y;
    //}

    Peak pk;
    pk.maxval= maxval;
    pk.pt=pt;
    return pk;
}  
person mrgloom    schedule 20.08.2012
comment
Обратите внимание, что сложное умножение можно выполнять только с тремя умножениями вместо четырех. Хотя я не думаю, что это будет сильно влиять на скорость. mathworld.wolfram.com/ComplexMultiplication.html - person CookieOfFortune; 08.10.2012

Вам нужно будет использовать 2D FFT, если вы хотите вычислить фазовую корреляцию для двух изображений. Пока вам не нужно беспокоиться об использовании мудрости FFTW — просто используйте базовый 2D-план для БПФ, пока не заработаете. То же самое для многопоточности - сначала заставьте его работать в однопоточном режиме.

person Paul R    schedule 03.02.2012
comment
Я реализовал так (codepaste.ru/9225), но вроде работает не так, как 1D трансформировать. - person mrgloom; 03.02.2012
comment
Я не вижу ничего явно неправильного — попробуйте протестировать его с помощью img1 == img2 — вы должны получить один пик в точке (0,0). - person Paul R; 03.02.2012
comment
Это дает мне (0,0), если я назову это как phase_correlation2D(src, src, res); но это дает мне не такой результат, как Phase_correlation1D на реальных тестовых изображениях. Также, кстати, 2D-преобразование выполняется быстрее. - person mrgloom; 03.02.2012
comment
Забудьте об одномерной версии — пытаться выполнить фазовую корреляцию двумерных изображений с одномерным БПФ бессмысленно. - person Paul R; 03.02.2012
comment
Хорошо, использую 2D версию(сейчас она немного изменилась и работает нормально) (codepaste.ru/9226 ) а как его запустить во многих потоках? Я использую fftw_init_threads(); перед любым вызовом FFTW и fftw_plan_with_nthreads(N); перед настройкой планов FFTW, но если N!=1, то происходит сбой на 1-м fftw_execute. - person mrgloom; 03.02.2012
comment
Прежде чем вы начнете использовать потоки, вы, вероятно, захотите провести рефакторинг и общие улучшения. Например. в общем, вы должны создавать и уничтожать планы только один раз, а затем повторно использовать одни и те же планы каждый раз, когда вы вызываете свою функцию. Создание планов стоит дорого по сравнению с фактическим fftw_execute. Также для лучшей производительности вы можете использовать как минимум FFTW_MEASURE при первом создании планов. То же самое для распределения памяти. - person Paul R; 03.02.2012
comment
Хорошо, я сделал все оптимизации, которые вы советуете. (Странно, но FFTW_MEASURE дает худший результат). Но я не могу использовать параллелизм, реализованный в FFTW внутри (он ломается на fftw_execute, если я использую более 1 протектора (возможно, я неправильно скомпилировал FFTW)), а также не могу использовать потоки Boost (он работает, но дает неправильный результат любой время разное). - person mrgloom; 06.02.2012
comment
fftw_plan_dft_2d занимает больше времени, если вы используете FFTW_MEASURE — вот почему вам нужно переместить его из цикла в процедуру инициализации, где он будет вызываться только один раз — тогда вы сможете повторно использовать один и тот же план каждый раз, когда выполняете БПФ. - person Paul R; 06.02.2012
comment
Нет, я уже удалил его из вашего цикла, но он работает дольше, может быть, потому, что он запускает некоторые тесты, а я работаю в Windows и использую некоторое процессорное время, помимо моего приложения, использующего FFTW. Кстати мне удалось заставить FFTW работать с boost thread, решение было такое - у каждого потока должны быть свои планы и память. Спасибо за ваши советы в любом случае. - person mrgloom; 07.02.2012
comment
Еще один вопрос: мне нужно применить окно Хэмминга (или другую оконную функцию). Я нашел некоторую информацию здесь en. wikipedia.org/wiki/Hamming_window#Hamming_window, но не могу понять. Мне нужно сделать это перед БПФ? Также я не могу понять, что w, n, N в формуле? - person mrgloom; 20.02.2012