openCV 的 fftshift C++ 实现

fftshift c++ implemetation for openCV

这个问题我已经看过了 fftshift/ifftshift C/C++ source code

我正在尝试从 matlab 实现 fftshift

这是来自一维数组

的matlab函数的代码
numDims = ndims(x);
    idx = cell(1, numDims);
    for k = 1:numDims
        m = size(x, k);
        p = ceil(m/2);
        idx{k} = [p+1:m 1:p];
    end
y = x(idx{:});

我的 c++/openCV 代码是,fftshift 基本上做的是从某个枢轴位置交换值。
因为我似乎无法理解 opencv 中如何为复数构建矩阵。
它在这里说
http://docs.opencv.org/modules/core/doc/operations_on_arrays.html#dft
CCS(复共轭对称
我认为将复数拆分为实数和虚数并交换它们会更容易。然后合并回一个矩阵。

cv::vector<float> distanceF (f.size());

//ff = fftshift(ff);
cv::Mat ff;
cv::dft(distanceF, ff, cv::DFT_COMPLEX_OUTPUT);

//Make place for both the complex and the real values
cv::Mat planes[] = {cv::Mat::zeros(distanceF.size(),1, CV_32F), cv::Mat::zeros(distanceF.size(),1, CV_32F)};
cv::split(ff, planes);                   // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))

int numDims = ff.dims;
for (int i = 0; i < numDims; i++)
{
    int m = ff.rows;
    int p = ceil(m/2);

}

我的问题是因为我对 DFT 的输入是 vector<float> 我似乎无法创建平面垫来拆分复数?

你能想到一种更好的方法来交换 cv::mat 数据结构中的值吗?

这是供将来参考: 经过测试,对于 1D

位准确
    cv::Mat ff;
    cv::dft(distanceF, ff, cv::DFT_ROWS|cv::DFT_COMPLEX_OUTPUT);

    //Make place for both the complex and the real values
    cv::Mat planes[] = {cv::Mat::zeros(distanceF.size(),1, CV_32F), cv::Mat::zeros(distanceF.size(),1, CV_32F)};
    cv::split(ff, planes);                   // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))

    int m = planes[0].cols;
    int pivot = ceil(m/2);
    //duplicate FFT results with Complex conjugate in order to get exact matlab results

    for (int i = pivot + 1, k = pivot; i < planes[1].cols; i++, k--)
    {
         planes[1].at<float>(i) = planes[1].at<float>(k) * -1; 
         planes[0].at<float>(i) = planes[0].at<float>(k);
    }   

    //TODO maybe we need to see what happens for pair and odd ??
    float im  = planes[1].at<float>(0);
    float re = planes[0].at<float>(0);

    for (int i = 0; i < pivot; i++)
    {   
        //IM
        planes[1].at<float>(i) = planes[1].at<float>(pivot + i +1); 
        planes[1].at<float>(pivot +i +1) = planes[1].at<float>(i +1);

        //Real
        planes[0].at<float>(i) = planes[0].at<float>(pivot + i +1); 
        planes[0].at<float>(pivot +i +1) = planes[0].at<float>(i +1);
    }
    planes[1].at<float>(pivot) = im;
    planes[0].at<float>(pivot) = re;

在Matlab的实现中,主要代码是两行:

idx{k} = [p+1:m 1:p];
y = x(idx{:});

第一个获得正确的索引顺序与原来的相反;然后第二个根据索引顺序分配输出数组。因此,如果想在不进行数据交换的情况下重写Matlab的实现,就需要分配一个新的数组,对数组进行赋值。

使用 adjustROI 和 copyTo 代替 .at() 怎么样?它肯定会更有效率:

(对于您的一维案例):

Mat shifted(ff.size(),ff.type());
pivot = ff.cols / 2;
ff(Range::all(),Range(pivot + 1, ff.cols)).copyTo(shifted(Range::all(),Range(0,pivot)));
ff(Range::all(),Range(0,pivot+1)).copyTo(shifted(Range::all(),Range(pivot,ff.cols)));

对于 2D 情况,应添加两行,并修改行范围...

好的,此线程可能在此期间已过时,但可能对其他用户而言。看看示例:

opencv/samples/cpp/dft.cpp (line 66 - 80)

int cx = mag.cols/2;
int cy = mag.rows/2;

// rearrange the quadrants of Fourier image
// so that the origin is at the image center
Mat tmp;
Mat q0(mag, Rect(0, 0, cx, cy));
Mat q1(mag, Rect(cx, 0, cx, cy));
Mat q2(mag, Rect(0, cy, cx, cy));
Mat q3(mag, Rect(cx, cy, cx, cy));

q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);

q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);

我认为这是针对不同维度的一种简短而干净的方法。

我一直在基于这个 post 自己实现它,我使用了 Fabian 实现,它运行良好。但是当行数或列数为奇数时,就会出现问题,移位不正确。

然后您需要填充您的矩阵,然后删除多余的行或列。

   {bool flag_row = false;
    bool flag_col = false;

    if( (inputMatrix.rows % 2)>0)
    {
        cv::Mat row = cv::Mat::zeros(1,inputMatrix.cols, CV_64F);  
        inputMatrix.push_back(row);
        flag_row =true;
    }

    if( (inputMatrix.cols % 2)>0)
    {
        cv::Mat col = cv::Mat::zeros(1,inputMatrix.rows, CV_64F);  
        cv::Mat tmp;
        inputMatrix.copyTo(tmp);
        tmp=tmp.t();
        tmp.push_back(col);
        tmp=tmp.t();
        tmp.copyTo(inputMatrix);

        flag_col = true;
    }

    int cx = inputMatrix.cols/2.0;
    int cy = inputMatrix.rows/2.0;

    cv::Mat outputMatrix;
    inputMatrix.copyTo(outputMatrix);

    // rearrange the quadrants of Fourier image
    // so that the origin is at the image center
    cv::Mat tmp;
    cv::Mat q0(outputMatrix, cv::Rect(0, 0, cx, cy));
    cv::Mat q1(outputMatrix, cv::Rect(cx, 0, cx, cy));
    cv::Mat q2(outputMatrix, cv::Rect(0, cy, cx, cy));
    cv::Mat q3(outputMatrix, cv::Rect(cx, cy, cx, cy));

    q0.copyTo(tmp);
    q3.copyTo(q0);
    tmp.copyTo(q3);

    q1.copyTo(tmp);
    q2.copyTo(q1);
    tmp.copyTo(q2);

    int row = inputMatrix.rows;
    int col = inputMatrix.cols;
    if(flag_row)
    {
        outputMatrix = Tools::removerow(outputMatrix,row/2-1);
    }
    if(flag_col)
    {
        outputMatrix = Tools::removecol(outputMatrix,col/2-1);
    }

    return outputMatrix;

我知道,这是一个很旧的线程,但我今天在寻找移动 fft 结果的解决方案时发现了它。也许我在本网站和其他资源的帮助下编写的小功能可能对未来的读者在网上搜索并最终来到这里有所帮助。

bool FftShift(const Mat& src, Mat& dst)
{
  if(src.empty()) return true;

  const uint h=src.rows, w=src.cols;        // height and width of src-image
  const uint qh=h>>1, qw=w>>1;              // height and width of the quadrants

  Mat qTL(src, Rect(   0,    0, qw, qh));   // define the quadrants in respect to
  Mat qTR(src, Rect(w-qw,    0, qw, qh));   // the outer dimensions of the matrix.
  Mat qBL(src, Rect(   0, h-qh, qw, qh));   // thus, with odd sizes, the center
  Mat qBR(src, Rect(w-qw, h-qh, qw, qh));   // line(s) get(s) omitted.

  Mat tmp;
  hconcat(qBR, qBL, dst);                   // build destination matrix with switched
  hconcat(qTR, qTL, tmp);                   // quadrants 0 & 2 and 1 & 3 from source
  vconcat(dst, tmp, dst);

  return false;
}

这是我的做法(快速而肮脏,可以优化):

// taken from the opencv DFT example (see opencv/samples/cpp/dft.cpp within opencv v440 sourcecode package)
cv::Mat fftshift(const cv::Mat& mat){
    
    // create copy to not mess up the original matrix (ret is only a "window" over the provided matrix)
    cv::Mat cpy;
    mat.copyTo(cpy);

    // crop the spectrum, if it has an odd number of rows or columns
    cv::Mat ret = cpy(cv::Rect(0, 0, cpy.cols & -2, cpy.rows & -2));

    // rearrange the quadrants of Fourier image so that the origin is at the image center
    int cx = ret.cols/2;
    int cy = ret.rows/2;
    cv::Mat q0(ret, cv::Rect(0, 0, cx, cy));   // Top-Left - Create a ROI per quadrant
    cv::Mat q1(ret, cv::Rect(cx, 0, cx, cy));  // Top-Right
    cv::Mat q2(ret, cv::Rect(0, cy, cx, cy));  // Bottom-Left
    cv::Mat q3(ret, cv::Rect(cx, cy, cx, cy)); // Bottom-Right

    cv::Mat tmp; // swap quadrants (Top-Left with Bottom-Right)
    q0.copyTo(tmp);
    q3.copyTo(q0);
    tmp.copyTo(q3);
    q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)
    q2.copyTo(q1);
    tmp.copyTo(q2);

    return ret;
}

// reverse the swapping of fftshift. (-> reverse the quadrant swapping)
cv::Mat ifftshift(const cv::Mat& mat){

    // create copy to not mess up the original matrix (ret is only a "window" over the provided matrix)
    cv::Mat cpy;
    mat.copyTo(cpy);

    // crop the spectrum, if it has an odd number of rows or columns
    cv::Mat ret = cpy(cv::Rect(0, 0, cpy.cols & -2, cpy.rows & -2));

    // rearrange the quadrants of Fourier image so that the origin is at the image center
    int cx = ret.cols/2;
    int cy = ret.rows/2;
    cv::Mat q0(ret, cv::Rect(0, 0, cx, cy));   // Top-Left - Create a ROI per quadrant
    cv::Mat q1(ret, cv::Rect(cx, 0, cx, cy));  // Top-Right
    cv::Mat q2(ret, cv::Rect(0, cy, cx, cy));  // Bottom-Left
    cv::Mat q3(ret, cv::Rect(cx, cy, cx, cy)); // Bottom-Right

    cv::Mat tmp; // swap quadrants (Bottom-Right with Top-Left)
    q3.copyTo(tmp);
    q0.copyTo(q3);
    tmp.copyTo(q0);
    q2.copyTo(tmp); // swap quadrant (Bottom-Left with Top-Right)
    q1.copyTo(q2);
    tmp.copyTo(q1);

    return ret;
}