用于openCV的fftshift c++实现

dtcbnfnu  于 2022-12-05  发布在  其他
关注(0)|答案(8)|浏览(222)

我已经看过这个问题fftshift/ifftshift C/C++ source code
我正在尝试从matlab实现fftshift
这是来自matlab* 函数的代码 *,用于1D数组

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
复共轭对称
我想把复数分成真实的和虚数,交换它们,然后合并成一个矩阵会更容易。

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数据结构中的值吗?

waxmsbnn

waxmsbnn1#

好的,这个线程可能是过时的同时,但可能对其他用户。.看看示例:
(第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);

我认为这是一个简短而清晰的方式为不同的维度。

mtb9vblg

mtb9vblg2#

我知道,这是一个相当老的线程,但我发现它今天在寻找一个解决方案,以转移fft-result.也许我写的小函数的帮助下,这个网站和其他来源,可以帮助未来的读者搜索网络,并最终在这里了。

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;
}
pod7payv

pod7payv3#

使用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情况,应再添加两行,并修改行范围...

nwnhqdif

nwnhqdif4#

我一直在根据这篇文章自己实现它,我用的是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;
ca1c2owp

ca1c2owp5#

下面是我所做的(快速和肮脏,可以优化):

// 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;
}
tuwxkamq

tuwxkamq6#

以下内容供将来参考:经过测试,对于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;
6yt4nkrj

6yt4nkrj7#

在前面的答案中,没有任何实现可以正确处理奇数大小的图像。
fftshift将原点从左上方移动到中心(在size/2处)。ifftshift将原点从中心移动到左上方。这两个操作对于偶数尺寸是相同的,但对于奇数尺寸是不同的。
对于奇数大小,fftshift将前(size+1)/2个像素与剩余的size/2个像素交换,这会将索引为0的像素移动到size/2ifftshift执行相反的操作,用剩下的(size+1)/2像素交换第一个size/2像素。这段代码是我能想到的这两个操作的最简单的实现。(请注意,如果size为偶数,则(size+1)/2 == size/2为偶数。)

bool forward = true; // true for fftshift, false for ifftshift
cv::Mat img = ...;   // the image to process

// input sizes
int sx = img.cols;
int sy = img.rows;

// size of top-left quadrant
int cx = forward ? (sx + 1) / 2 : sx / 2;
int cy = forward ? (sy + 1) / 2 : sy / 2;

// split the quadrants
cv::Mat top_left(img, cv::Rect(0, 0, cx, cy));
cv::Mat top_right(img, cv::Rect(cx, 0, sx - cx, cy));
cv::Mat bottom_left(img, cv::Rect(0, cy, cx, sy - cy));
cv::Mat bottom_right(img, cv::Rect(cx, cy, sx - cx, sy - cy));

// merge the quadrants in right order
cv::Mat tmp1, tmp2;
cv::hconcat(bottom_right, bottom_left, tmp1);
cv::hconcat(top_right, top_left, tmp2);
cv::vconcat(tmp1, tmp2, img);

这段代码将整个图像复制两次,但实现起来简单快捷。一个性能更好的实现是就地交换值。This answer有正确的代码在一行上执行此操作,它必须应用于图像的每一列和每一行。

pprl5pva

pprl5pva8#

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

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

第一个获取相对于原始索引的正确索引顺序;然后第二个按照索引顺序对输出数组赋值,因此,如果你想重写Matlab的实现而不进行数据交换,你需要分配一个新的数组并对数组赋值。

相关问题