fftshift / ifftshift C / C ++源代码

时间:2011-05-06 17:50:35

标签: c++ c matlab fft

有没有人知道是否有任何免费和开源库以matlab中的方式实现这两个函数?

由于

10 个答案:

答案 0 :(得分:15)

FFTHIFT / IFFTSHIFT是一种做CIRCSHIFT的奇特方式。 您可以验证FFTSHIFT是否可以重写为CIRCSHIFT,如下所示。 您可以在C / C ++中定义宏以将FFTSHIFT转换为CIRCSHIFT。

A = rand(m, n);
mm = floor(m / 2);
nn = floor(n / 2);
% All three of the following should provide zeros.
circshift(A,[mm, nn]) - fftshift(A)
circshift(A,[mm,  0]) - fftshift(A, 1)
circshift(A,[ 0, nn]) - fftshift(A, 2) 

IFFTSHIFT可以找到类似的等价物。

使用以下代码可以非常简单地实现循环移位(可以使用并行版本的课程进行改进)。

template<class ty>
void circshift(ty *out, const ty *in, int xdim, int ydim, int xshift, int yshift)
{
  for (int i = 0; i < xdim; i++) {
    int ii = (i + xshift) % xdim;
    for (int j = 0; j < ydim; j++) {
      int jj = (j + yshift) % ydim;
      out[ii * ydim + jj] = in[i * ydim + j];
    }
  }
}

然后

#define fftshift(out, in, x, y) circshift(out, in, x, y, (x/2), (y/2))
#define ifftshift(out, in, x, y) circshift(out, in, x, y, ((x+1)/2), ((y+1)/2))

这是有点即兴的。如果有任何格式/语法问题,请耐心等待。

答案 1 :(得分:5)

通常,使用v(k)= v(k)*( - 1)** k来完成FFT的居中 时域。在频域中移位是一个糟糕的替代品 数学原因和计算效率。 见第27页: http://show.docjava.com/pub/document/jot/v8n6.pdf

我不确定为什么Matlab文档会像他们那样做, 他们没有提供技术参考。

答案 2 :(得分:5)

可能此代码可能有所帮助。它仅对一个缓冲区内的1D阵列执行fftshift / ifftshift。偶数个元素的前后移位算法完全相同。

void swap(complex *v1, complex *v2)
{
    complex tmp = *v1;
    *v1 = *v2;
    *v2 = tmp;
}

void fftshift(complex *data, int count)
{
    int k = 0;
    int c = (int) floor((float)count/2);
    // For odd and for even numbers of element use different algorithm
    if (count % 2 == 0)
    {
        for (k = 0; k < c; k++)
            swap(&data[k], &data[k+c]);
    }
    else
    {
        complex tmp = data[0];
        for (k = 0; k < c; k++)
        {
            data[k] = data[c + k + 1];
            data[c + k + 1] = data[k + 1];
        }
        data[c] = tmp;
    }
}

void ifftshift(complex *data, int count)
{
    int k = 0;
    int c = (int) floor((float)count/2);
    if (count % 2 == 0)
    {
        for (k = 0; k < c; k++)
            swap(&data[k], &data[k+c]);
    }
    else
    {
        complex tmp = data[count - 1];
        for (k = c-1; k >= 0; k--)
        {
            data[c + k + 1] = data[k];
            data[k] = data[c + k];
        }
        data[c] = tmp;
    }
}

更新: 也可以在Optolithium(在OptolithiumC / libs / fourier下)找到任意点数的FFT库(包括fftshift操作)

答案 3 :(得分:4)

或者您可以通过键入type fftshift并在C ++中重新编码来自己完成。 Matlab代码并不复杂。

编辑:我注意到这个答案最近已经被评了几次,并以负面的方式评论。我记得type fftshift比当前实施更具启发性的时间,但我可能错了。如果我可以删除答案,我认为它似乎不再相关。

Here是一个版本(由Octave提供),无需实现它 circshift

答案 4 :(得分:3)

我测试了这里提供的代码并制作了一个示例项目来测试它们。对于1D代码,可以使用std::rotate

template <typename _Real>
static inline
void rotshift(complex<_Real> * complexVector, const size_t count)
{
    int center = (int) floor((float)count/2);
    if (count % 2 != 0) {
        center++;
    }
    // odd: 012 34 changes to 34 012
    std::rotate(complexVector,complexVector + center,complexVector + count);
}

template <typename _Real>
static inline
void irotshift(complex<_Real> * complexVector, const size_t count)
{
    int center = (int) floor((float)count/2);
    // odd: 01 234 changes to 234 01
    std::rotate(complexVector,complexVector +center,complexVector + count);
}

由于其简单性,我更喜欢使用std::rotate代替来自Alexei的代码。

对于2D来说,它变得更加复杂。对于偶数,它基本上是向左翻转并翻转倒置。对于奇数,它是circshift算法:

//    A =
//    1     2     3
//    4     5     6
//    7     8     9


//    fftshift2D(A)
//    9  |   7     8
//    --------------
//    3  |   1     2
//    6  |   4     5


//    ifftshift2D(A)
//    5     6   |  4
//    8     9   |  7
//    --------------
//    2     3   |  1

这里我实现了带有接口的circshift代码,只使用一个数组进行输入和输出。对于偶数,只需要一个数组,对于奇数,临时创建第二个数组并将其复制回输入数组。由于复制阵列所需的额外时间,这会导致性能下降。

template<class _Real>
static inline
void fftshift2D(complex<_Real> *data, size_t xdim, size_t ydim)
{
    size_t xshift = xdim / 2;
    size_t yshift = ydim / 2;
    if ((xdim*ydim) % 2 != 0) {
        // temp output array
        std::vector<complex<_Real> > out;
        out.resize(xdim * ydim);
        for (size_t x = 0; x < xdim; x++) {
            size_t outX = (x + xshift) % xdim;
            for (size_t y = 0; y < ydim; y++) {
                size_t outY = (y + yshift) % ydim;
                // row-major order
                out[outX + xdim * outY] = data[x + xdim * y];
            }
        }
        // copy out back to data
        copy(out.begin(), out.end(), &data[0]);
        }
    else {
        // in and output array are the same,
        // values are exchanged using swap
        for (size_t x = 0; x < xdim; x++) {
            size_t outX = (x + xshift) % xdim;
            for (size_t y = 0; y < yshift; y++) {
                size_t outY = (y + yshift) % ydim;
                // row-major order
                swap(data[outX + xdim * outY], data[x + xdim * y]);
            }
        }
    }
}

template<class _Real>
static inline
void ifftshift2D(complex<_Real> *data, size_t xdim, size_t ydim)
{
    size_t xshift = xdim / 2;
    if (xdim % 2 != 0) {
        xshift++;
    }
    size_t yshift = ydim / 2;
    if (ydim % 2 != 0) {
        yshift++;
    }
    if ((xdim*ydim) % 2 != 0) {
        // temp output array
        std::vector<complex<_Real> > out;
        out.resize(xdim * ydim);
        for (size_t x = 0; x < xdim; x++) {
            size_t outX = (x + xshift) % xdim;
            for (size_t y = 0; y < ydim; y++) {
                size_t outY = (y + yshift) % ydim;
                // row-major order
                out[outX + xdim * outY] = data[x + xdim * y];
            }
        }
        // copy out back to data
        copy(out.begin(), out.end(), &data[0]);
        }
    else {
        // in and output array are the same,
        // values are exchanged using swap
        for (size_t x = 0; x < xdim; x++) {
            size_t outX = (x + xshift) % xdim;
            for (size_t y = 0; y < yshift; y++) {
                size_t outY = (y + yshift) % ydim;
                // row-major order
                swap(data[outX + xdim * outY], data[x + xdim * y]);
            }
        }
    }
}

答案 5 :(得分:1)

注意:提供了更好的答案,我只是暂时搁置一段时间......我不知道是什么。

试试这个:

template<class T> void ifftShift(T *out, const T* in, size_t nx, size_t ny)
{
    const size_t hlen1 = (ny+1)/2;
    const size_t hlen2 = ny/2;
    const size_t shft1 = ((nx+1)/2)*ny + hlen1;
    const size_t shft2 = (nx/2)*ny + hlen2;

    const T* src = in;
    for(T* tgt = out; tgt < out + shft1 - hlen1; tgt += ny, src += ny) { // (nx+1)/2 times
        copy(src, src+hlen1, tgt + shft2);          //1->4
        copy(src+hlen1, src+ny, tgt+shft2-hlen2); } //2->3
    src = in;
    for(T* tgt = out; tgt < out + shft2 - hlen2; tgt += ny, src += ny ){ // nx/2 times
        copy(src+shft1, src+shft1+hlen2, tgt);         //4->1
        copy(src+shft1-hlen1, src+shft1, tgt+hlen2); } //3->2
};

对于具有偶数维的矩阵,您可以就地执行此操作,只需将相同的指针传递到输入和输出参数。

另请注意,对于1D数组,fftshift只是std :: rotate。

答案 6 :(得分:1)

您还可以使用arrayfire的shift函数替换Matlab的circshift并重新实现其余代码。如果您对AF的任何其他功能感兴趣(例如通过简单地更改链接器标志来移植到GPU),这可能很有用。

但是,如果您的所有代码都要在CPU上运行并且非常复杂,或者您不想使用任何其他数据格式(AF需要af :: arrays),请坚持使用其他选项之一。

我最终改为AF,因为我不得不重新将fftshift作为OpenCL内核重新实现,否则就会回来。

答案 7 :(得分:0)

Octave uses fftw实施(i)fftshift。

答案 8 :(得分:0)

它会给matlab中的ifftshift提供相同的结果

ifftshift(vector< vector <double> > Hlow,int RowLineSpace, int ColumnLineSpace)
{
   int pivotRow=floor(RowLineSpace/2);
   int pivotCol=floor(ColumnLineSpace/2);

   for(int i=pivotRow;i<RowLineSpace;i++){
      for(int j=0;j<ColumnLineSpace;j++){
        double temp=Hlow.at(i).at(j);
        second.push_back(temp);
      }
    ifftShiftRow.push_back(second);
    second.clear();
}

for(int i=0;i<pivotRow;i++){
        for(int j=0;j<ColumnLineSpace;j++){
            double temp=Hlow.at(i).at(j);
            first.push_back(temp);
        }
        ifftShiftRow.push_back(first);
        first.clear();
    }


 double** arr = new double*[RowLineSpace];
  for(int i = 0; i < RowLineSpace; ++i)
      arr[i] = new double[ColumnLineSpace];
  int i1=0,j1=0;
  for(int j=pivotCol;j<ColumnLineSpace;j++){
        for(int i=0;i<RowLineSpace;i++){
            double temp2=ifftShiftRow.at(i).at(j);
            arr[i1][j1]=temp2;
            i1++;
        }
        j1++;
        i1=0;
    }
 for(int j=0;j<pivotCol;j++){
    for(int i=0;i<RowLineSpace;i++){
        double temp1=ifftShiftRow.at(i).at(j);
            arr[i1][j1]=temp1;
                i1++;

            }

           j1++;
            i1=0;
        }

for(int i=0;i<RowLineSpace;i++){
    for(int j=0;j<ColumnLineSpace;j++){
        double value=arr[i][j];
        temp.push_back(value);

    }
    ifftShiftLow.push_back(temp);
    temp.clear();
}
    return ifftShiftLow;


 }

答案 9 :(得分:-1)

您可以使用kissfft。它速度快,使用极其简单,而且免费。像你想要的那样排列输出只需要:

a)通过(-dim_x / 2,-dim_y / 2,...)移动,具有周期性边界条件

b)FFT或IFFT

c)使用周期性边界条件向后移动(dim_x / 2,dim_y / 2,...)

d)规模? (根据您的需要,IFFT * FFT将按dim_x * dim_y * ...默认扩展功能)

相关问题