Дискретное преобразование Фурье OpenCV

OpenCV

Дискретное преобразование Фурье (ДПФ)

определение

Дискретное преобразование Фурье (дискретное преобразование Фурье, сокращенно ДПФ) — это дискретная форма преобразования Фурье как во временной, так и в частотной областях, которая преобразует выборку сигнала во временной области в его выборку ДПФ в частотной области.

傅里叶变换

Для последовательности из N точек {X[n]} (0

离散傅里叶变换

функция дфт()

Роль функции dft() заключается в выполнении прямого или обратного дискретного преобразования Фурье для одномерного или двумерного массива чисел с плавающей запятой.

函数原型

void dft(
    InoutArray src,  //输入矩阵
    OutputArray dst, ///输出矩阵
    int flags = 0,  //转换的标识符
    int onozeroRows,  
)

Третий параметр, преобразованный идентификатор делится на:

  • DFT_INVERSE заменяет прямое преобразование по умолчанию одномерным или двумерным обратным преобразованием.
  • DFT_SCALE Идентификатор масштабирования, выходные результаты будут масштабированы на 1/N.
  • DFT_CMPLEX_OUTPUT, DFT_REAL_OUTPUT Выполнение обратного преобразования одномерного или двумерного массива.

Возвращает оптимальный размер ДПФ: функция getOptimalDFTSize()

void int getOptimalDFTSize(int vecsize);

Расширьте границы изображения: функция copyMakeBorder()

void copyMakeBorder(
    InputArray src,  //输入图像
    OutputArray dst,  //输出图像
    int top,  //在图像上方扩充的像素值
    int bottom,  //在图像下方扩充的像素值
    int left,  //在图像左方扩充的像素值
    int right,  //在图像右方扩充的像素值
    int borderType, //边界类型·
    const Scalars,
)

Вычислить величину двумерного вектора: функция величина()

Используется для вычисления величины двумерного вектора.

void magnitude(
    InputArray x,  //表示矢量的浮点型X坐标值,即实部
    InputArray y,  //表示矢量的浮点型Y坐标值,即虚部
    OutputArray magnitude,  //输出的幅值
)

Вычислить натуральный логарифм: функция log()

Функция функции log() заключается в вычислении натурального логарифма абсолютного значения каждого элемента массива.

void log(
    InputArray src,
    OutputArray dst,
);

原理即为:

if(src(I) != 0)
    log|src(I)|
else
    C

Нормализация матрицы: функция normalize()

void normalize(
    InputArray src,
    OutputArray dst,
    double alpha = 1,  //归一化之后的最大值,有默认值1
    double beta = 0,  //归一化之后的最大值,有默认值0
    int norm_type = NORM_L2,  //归一化类型
    int dtype = -1,   //为负数时输出矩阵和src有同样的类型,否则,它和src有同样的通道数,深度为CV_MAT_DEPTH
    InputArray mask=noArray(),  //可选的操作掩模
)

функция getOptimalDFTSize

Эта функция возвращает оптимальный размер Фурье заданного размера вектора. Для увеличения скорости работы дискретного преобразования Фурье необходимо расширить изображение.

Пример основного фрагмента для вычисления свертки двух реальных 2D-матриц с использованием функции dft

  • где роль MulSpectrums заключается в вычислении произведения каждого элемента двух спектров Фурье.
void convolveDft(InputArray A, InputArray B, OutputArray C)
{
    //初始化输出矩阵
    C.create(abs(A.rows - B.rows) + 1, abs(A.cols - B.cols) + 1, A.type);

    //计算DFT变换的尺寸
    dftSize.width = getOptimalDFTSize(A.cols + B.cols - 1);
    dftSize.height = getOptimalDFTSize(A.rows + B.rows - 1);

    //分配临时缓冲区并初始化置0
    Mat tempA(dftSize,A.type(),Scalar::all(0));
    Mat tempB(dftSize,B.type(),Scalar::all(0));

    //分别复制A和B到tempA和tempB的左上角
    Mat roiA(tempA,Rect(0,0,A.cols,A.rows));
    A.copyTo(roiA);
    Mat roiB(tempB,Rect(0,0,B.cols,B.rows));
    B.copyTO(roiB);

    //就地操作,进行快速傅里叶变换,并将nonzeroRows 参数置为零,以进行更加快速的处理。
    dft(tempA,tempA,0,A.rows);
    dft(tempB,tempB,0,B.rows);

    //将得到的频谱相乘,结果存放于tempA当中
    mulSpectrums(tempA,tempB,tempA);  

    //将结果变换为频域,尽管结果行(result.rows)都为非零,我们只需其中的C.rows的第一行,所以采用nonzeroRows == C.rows
    dft(tempA,tempA,EFT_INVERSE + EFT_SCALE,C.rows);

    //将结果复制到C当中
    tempA(Rect(0,0,C.cols,C.rows)).copyTo(C);

    //所有的临时缓存区将被自动释放,所以无需收尾操作
}

Пример программы: дискретное преобразование Фурье

[1] Загрузите исходное изображение

//【1】ui灰度模式读取原始图像并显示
Mat srcImage = imread("D:\\Desktop\\lena.jpg",0);
if (!srcImage.data)
{
    cout << "lena图片读取错误" << endl;
    return false;
}
imshow("原始图像", srcImage);

[2] Увеличить изображение до нужного размера

//【2】将输入的图像延扩到最佳的尺寸,边界用0补充
int m = getOptimalDFTSize(I.rows);
int n = getOptimalDFTSize(I.cols);
//将添加的像素初始化为0
Mat padded;
copyMakeBorder(I,padded,0,m - I,rows,0,n - I.cols,BORDER_CONSTANT,Scalar::all(0));

[3] Выделить место для хранения результата преобразования Фурье

Результатом преобразования Фурье является комплексное число, и для каждого исходного значения изображения результат будет иметь два значения изображения.

为傅里叶变换的结果(实部和虚部)分配储存空间
//将planes数组组合合并成一个多通道的数组complexI
Mat planes[] = (Mat_<float>(padded),Mat::zeros(padded.size(),CV_32F));
Mat complexI;
merge(planes,2,complexI);

[4] Выполнение дискретного преобразования Фурье

def(complexI,complexI);

[5] Преобразование комплексных чисел в величины

Результатом дискретного преобразования Фурье является комплексное число, и соответствующая величина может быть выражена как:

复数变幅值

//将复数转换为幅值,即=> log(1 + sqrt(Re(DFT(I)) ^ 2 + Im(DFT(I)) ^ 2))
split(complexI,planes);  //将多通道complexI分离成几个单通道数组
planes[0] = Re(DFT(I),planes[1] = Im(DFT(I))
magnitude(planes[0],planes[1],planes[0]);
Mat magnitudeImage = planes[0];

[6] Выполнить логарифмическое масштабирование

Величина преобразования Фурье слишком велика, чтобы уместиться на экране. Высокое значение отображается в виде белой точки на экране, а низкое значение — в виде черной точки, и изменение высоких и низких значений не может быть эффективно оценено. Чтобы подчеркнуть непрерывность высоких и низких изменений на экране, вместо линейной шкалы можно использовать логарифмическую шкалу по следующей формуле:

尺度缩放

//进行对数尺度缩放
magnitudeImage += Scalar::all(1);
log(magnitudeImage,magnitudeImage);  //求自然对数

[7] Отсечение и перераспределение квадрантов изображения магнитуды

Поскольку на втором шаге изображение было расширено, пришло время отсеять вновь добавленные пиксели. Позиции квадрантов изображения могут быть перераспределены для удобства отображения.

//若有奇数行或技术列,进行谱写裁剪
magnitudeImage = magnitudeImage(Rect(0,0,magnitudeImage.cols & -2,magnitudeImage.rows & -2));
//重新排列傅里叶图像中的象限,使得原点位于图像中心
int cx = magnitudeImage.cols / 2;
int cy = magnitudeImage.rows / 2;
Mat q0(magnitudeImage,Rect(0,0,cx,cy)); //ROI区域的左上
Mat q1(magnitudeImage,Rect(cx,0,cx,cy));  //ROI区域的右上
Mat q2(magnitudeImage,Rect(0,cy,cx,cy));  //ROI区域的左下
Mat q3(magnitudeImage,Rect(cx,cy,cx,cy));  //ROI区域的右下

//交换象限(左上与右下)
Mat tmp;
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);

//交换象限(右上与左下)
q1.copyTo(tmp);
q4.copyTo(q1);
tmp.copyTo(q4);

[8] Нормализация

Теперь у нас есть карта перераспределенных величин, но значение величины все еще превышает отображаемый диапазон [0,1], мы можем использовать функцию normalize() для нормализации до отображаемого диапазона.

normalize(magnitudeImage,magnitudeImage,0,1,NORM_MINMAX);

【9】Эффект отображения

imshow("频谱幅值",magnitudeImage);

Эффект

效果