图像处理之频率域与空间域转换(C++)
前言
频率特征是图像的灰度变化特征。一个重要的经验结论:低频代表图像整体轮廓,高频代表了图像噪声,中频代表图像边缘、纹理等细节。
傅里叶变换进行频域分析的使用场景:
1.具有纹理(条纹)特征的图像,如布匹、木板、纸张等。
2.需要提取对比度低或者信噪比低的特征。
3.图像尺寸较大或者卷积核尺寸大进行滤波时,此时需要转换至频域计算,提高计算速度。(原理:空间域的卷积等价于频率域相乘)
一、原理
基于opencv库实现的空间域和频率域转换,方便进一步对频域分析。
二、空间域到频率域转换
1.代码实现
/*
* @param cv::Mat src 输入图片
* @param cv::Mat dst 输出图片
* @brief 空间域转换到频率域
*/
void Spatial2Frequency(cv::Mat& src,cv::Mat& dst)
{
// 图像尺寸修正,获得最优的傅里叶变换的尺寸,加快计算
int h = cv::getOptimalDFTSize(src.rows);
int w = cv::getOptimalDFTSize(src.cols);
cv::Mat padImg;
cv::copyMakeBorder(src, padImg, 0, h - src.rows, 0, w - src.cols, CV_HAL_BORDER_CONSTANT, cv::Scalar(0));
//构造Mat矩阵的数组存储傅里叶变换后的实数矩阵(planes[0])、虚数矩阵(planes[1])
cv::Mat planes[] = { cv::Mat_<float>(padImg),cv::Mat::zeros(padImg.size(),CV_32FC1) };
cv::Mat dftImg;
cv::merge(planes, 2, dftImg);
//执行离散傅里叶变换
cv::dft(dftImg, dftImg);
//分离dftImg,此时实数矩阵(planes[0])、虚数矩阵(planes[1])
cv::split(dftImg, planes);
//定义幅度谱和相位谱
cv::Mat phaseImg, magnitudeImg;
cv::phase(planes[0], planes[1], phaseImg); //此处phaseImg的元素数据类型和planes[0]相同,都是float
cv::magnitude(planes[0], planes[1], magnitudeImg);//此处magnitudeImg的元素数据类型和planes[0]相同,都是float
planes[0] = magnitudeImg.clone();
cv::log(planes[0] + cv::Scalar(1), planes[0]); //使用对数变换压缩像素值范围,便于可视化
//交换四个频谱位置进行中心化
cv::resize(dst, dst, planes[0].size());
//对四个频谱位置进行交换
planes[0](cv::Rect(0, 0, planes[0].cols / 2, planes[0].rows / 2)).copyTo(dst(cv::Rect(planes[0].cols / 2, planes[0].rows / 2, planes[0].cols / 2, planes[0].rows / 2)));//左上到右下
planes[0](cv::Rect(planes[0].cols / 2, 0, planes[0].cols / 2, planes[0].rows / 2)).copyTo(dst(cv::Rect(0, planes[0].rows / 2, planes[0].cols / 2, planes[0].rows / 2)));//右上到左下
planes[0](cv::Rect(0, planes[0].rows / 2, planes[0].cols / 2, planes[0].rows / 2)).copyTo(dst(cv::Rect(planes[0].cols / 2, 0, planes[0].cols / 2, planes[0].rows / 2)));//左下到右上
planes[0](cv::Rect(planes[0].cols / 2, planes[0].rows / 2, planes[0].cols / 2, planes[0].rows / 2)).copyTo(dst(cv::Rect(0, 0, planes[0].cols / 2, planes[0].rows / 2)));//右下到左上
cv::normalize(dst, dst, 0, 255, cv::NORM_MINMAX, CV_8UC1);
}
int main()
{
//读取图片
string filepath = "F://work_study//algorithm_demo//baby.jpg";
cv::Mat src = cv::imread(filepath, cv::IMREAD_GRAYSCALE);
if (src.empty())
{
std::cout << "imread error" << std::endl;
return -1;
}
cv::Mat dst(src.size(), src.type());
Spatial2Frequency(src, dst);
//保存图片
imwrite("dst.bmp", dst);
cv::waitKey(0);
return 0;
}
2.结果展示
二、频率域到空间域转换
1.代码实现
/*
* @param cv::Mat src 输入图片
* @param cv::Mat dst 输出图片
* @brief 频率域转换到空间域
*/
void Frequency2Spatial(cv::Mat& src, cv::Mat& dst)
{
// 图像尺寸修正,获得最优的傅里叶变换的尺寸,加快计算
int h = cv::getOptimalDFTSize(src.rows);
int w = cv::getOptimalDFTSize(src.cols);
cv::Mat padImg;
cv::copyMakeBorder(src, padImg, 0, h - src.rows, 0, w - src.cols, CV_HAL_BORDER_CONSTANT, cv::Scalar(0));
//构造Mat矩阵的数组存储傅里叶变换后的实数矩阵(planes[0])、虚数矩阵(planes[1])
cv::Mat planes[] = { cv::Mat_<float>(padImg),cv::Mat::zeros(padImg.size(),CV_32FC1) };
cv::Mat dftImg;
cv::merge(planes, 2, dftImg);
//执行离散傅里叶变换
cv::dft(dftImg, dftImg);
//分离dftImg,此时实数矩阵(planes[0])、虚数矩阵(planes[1])
cv::split(dftImg, planes);
//定义幅度谱和相位谱
cv::Mat phaseImg, magnitudeImg;
cv::phase(planes[0], planes[1], phaseImg); //此处phaseImg的元素数据类型和planes[0]相同,都是float
cv::magnitude(planes[0], planes[1], magnitudeImg);//此处magnitudeImg的元素数据类型和planes[0]相同,都是float
//傅里叶逆变换,实现从频率域到空间域转换
cv::Mat idftImg;
//通过幅度谱和相位谱恢复实部planes[0]虚部planes[1]
//cv::polarToCart(magnitudeImg, phaseImg, planes[0], planes[1]);
cv::merge(planes, 2, idftImg);
cv::dft(idftImg, idftImg, CV_HAL_DFT_INVERSE | CV_HAL_DFT_REAL_OUTPUT);
cv::normalize(idftImg, idftImg, 0, 255, cv::NORM_MINMAX, CV_8U);
dst=idftImg(cv::Rect(0, 0, src.cols & -2, src.rows & -2)); //此处按位与的意思是为了把长宽调整为2的倍数
}
int main()
{
//读取图片
string filepath = "F://work_study//algorithm_demo//baby.jpg";
cv::Mat src = cv::imread(filepath, cv::IMREAD_GRAYSCALE);
if (src.empty())
{
std::cout << "imread error" << std::endl;
return -1;
}
cv::Mat dst(src.size(), src.type());
Frequency2Spatial(src,dst);
//保存图片
cv::imwrite("idftImg.bmp", dst);
cv::waitKey(0);
return 0;
}
2.结果展示
总结
本文主要实现了在opencv库使用C++下实现空间域和频率域的相互转换,后续更新空间域转换到频率域的应用。
因为笔者水平有限,有错误欢迎指出,代码本人均在本地运行实验正确,大家放心使用。