掘金 人工智能 05月18日 19:43
OpenCV 笔记(39):频域中的拉普拉斯算子
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入探讨了拉普拉斯算子在频域中的应用。首先回顾了拉普拉斯算子的定义及其傅里叶变换的推导过程,展示了如何在频域中实现空间域的微分操作。接着,通过OpenCV C++代码示例,演示了如何利用频域拉普拉斯算子进行图像锐化,并解释了为什么频域拉普拉斯响应不适合直接显示,以及幅度谱可视化的意义。最后,总结了拉普拉斯算子在图像增强、边缘检测等方面的应用,强调了其在图像处理中的重要作用。

🧮 拉普拉斯算子在二维空间中是对函数f(x,y)的二阶偏导数之和,公式为∇²f(x, y) = ∂²f(x, y)/∂x² + ∂²f(x, y)/∂y²。文章推导了拉普拉斯算子的傅里叶变换,得出在频域中,拉普拉斯算子的作用等价于对F(u,v)乘以一个因子H(u,v) = -(2π)²(u²+v²)。

🚀 在频域中应用拉普拉斯算子可以实现图像锐化。锐化的公式为Fsharpened(u,v) = F(u,v) + k⋅∇²F(u,v),其中k是一个控制锐化强度的正数。从空间域视角来看,这等价于fsharpened(x,y) = f(x,y) - k⋅∇²f(x,y)。

💻 文章提供了一个使用OpenCV C++实现频域拉普拉斯算子的例子,展示了如何创建频域拉普拉斯滤波器,并将其应用于图像锐化。代码中包括了创建拉普拉斯滤波器、进行傅里叶变换、应用滤波器、进行傅里叶逆变换以及锐化图像的步骤。

📈 由于拉普拉斯响应具有正负值对称性和极大的值域动态范围,因此频域拉普拉斯响应通常不适合直接显示。文章解释了为什么拉普拉斯响应是中间量,本身不代表直观图像内容,并建议展示幅度谱作为一种可视化方式。

1. 拉普拉斯算子

在该系列的第八篇文章中,我们曾经介绍过在二维空间拉普拉斯算子的定义为:

2f(x,y)=2f(x,y)x2+2f(x,y)y2\nabla^{2} f(x, y)=\frac{\partial^{2} f(x, y)}{\partial x^{2}}+\frac{\partial^{2} f(x, y)}{\partial y^{2}}

这是对函数 f(x,y)f(x,y) 的二阶偏导数之和。

2. 拉普拉斯算子的傅里叶变换及其推导

在该系列的第三十二篇文章中,我们曾给介绍过下面的公式

二维连续傅里叶变换:

F{f(x,y)}=F(u,v)=f(x,y)ej2π(ux+vy)dxdy\mathcal{F}\{f(x, y)\} = F(u,v)=\int_{-\infty}^{\infty}\int_{-\infty}^{\infty}f(x,y)e^{-j2\pi(u x+v y)}\,dxdy

二维连续傅里叶逆变换:

F1{F(u,v)}=f(x,y)=F(u,v)ej2π(ux+vy)dudv\mathcal{F}^{-1}\{F(u,v)\} = f(x,y)=\int_{-\infty}^{\infty}\int_{-\infty}^{\infty}F(u,v)e^{j2\pi(u x+v y)}\,du dv

下面,我们尝试推导一下拉普拉斯算子在频域中的表示。首先考虑傅里叶变换的性质,特别是微分运算的傅里叶变换性质:

F{f(x,y)x}=j2πuF(u,v)\mathcal{F}\left\{\frac{\partial f(x, y)}{\partial x}\right\} = j2\pi u F(u, v)

F{2f(x,y)2x}=(2πu)2F(u,v)\mathcal{F}\left\{\frac{\partial^2 f(x, y)}{\partial^2 x}\right\} = -(2\pi u)^2 F(u, v)

同理,

F{2f(x,y)2y}=(2πv)2F(u,v)\mathcal{F}\left\{\frac{\partial^2 f(x, y)}{\partial^2 y}\right\} = -(2\pi v)^2 F(u, v)

现在,可以直接将拉普拉斯算子作用在 f(x,y)f(x,y) 上的结果进行傅里叶变换:

F{2f(x,y)}=F{2f(x,y)2x+2f(x,y)2y}=(2πu)2F(u,v)(2πv)2F(u,v)=(2π)2(u2+v2)F(u,v)\mathcal{F}\{∇^2f(x,y)\} = \mathcal{F}\{\frac{\partial^2 f(x, y)}{\partial^2 x}+\frac{\partial^2 f(x, y)}{\partial^2 y}\}\\ = -(2\pi u)^2 F(u, v) -(2\pi v)^2 F(u, v) \\ = -(2\pi)^2(u^2+v^2)F(u, v)

在频域中,由之前介绍过的公式可知 f(x,y)f(x,y) 的傅里叶变换是 F(u,v)F(u,v),即 F{f(x,y)}=F(u,v)\mathcal{F}\{f(x, y)\} = F(u,v)

拉普拉斯算子的作用等价于对 F(u,v)F(u,v) 乘以一个因子:H(u,v)=(2π)2(u2+v2)H(u,v)=−(2π)^2(u^2+v^2)

这个乘积意味着:频率越高(u 或 v 越大),乘的因子绝对值越大,也就是高频成分被放大。因此频域中的拉普拉斯算子常用于边缘检测、锐化等图像处理任务。

3. 使用 OpenCV C++ 实现频域的拉普拉斯算子

下面的例子,实现频域拉普拉斯算子,并且对原图进行锐化。

在频域中,锐化操作相当于将原图频谱与拉普拉斯响应叠加。具体公式为:

Fsharpened (u,v)=F(u,v)+k2F(u,v)F_{\text {sharpened }}(u, v)=F(u, v)+k \cdot \nabla^{2} F(u, v)

其中 k 是一个控制锐化强度的正数。从空间域视角,这等价于:fsharpened (x,y)=f(x,y)k2f(x,y)f_{\text {sharpened }}(x, y)=f(x, y)-k \cdot \nabla^{2} f(x, y)

#include <opencv2/opencv.hpp>#include <iostream>using namespace cv;using namespace std;// 创建频域拉普拉斯滤波器Mat createLaplacianFilter(Size size) {    Mat laplacianFilter = Mat::zeros(size, CV_32FC2);    int center_row = laplacianFilter.rows / 2;    int center_col = laplacianFilter.cols / 2;    float factor = (2 * CV_PI) * (2 * CV_PI); // 计算 (2*PI)^2    for (int i = 0; i < laplacianFilter.rows; i++) {        for (int j = 0; j < laplacianFilter.cols; j++) {            float u = (float)(i - center_row);            float v = (float)(j - center_col);            float d_squared = u * u + v * v;            laplacianFilter.at<Vec2f>(i, j)[0] = -factor * d_squared; // −(2π)^2(u^2+v^2)            laplacianFilter.at<Vec2f>(i, j)[1] = 0;        }    }    return laplacianFilter;}// 对单个通道应用频域拉普拉斯锐化并返回处理后频域图像的幅度谱Mat processChannelAndGetProcessedMag(const Mat& src, Mat& dst, double alpha) {    int m = getOptimalDFTSize(src.rows);    int n = getOptimalDFTSize(src.cols);    Mat padded;    copyMakeBorder(src, padded, 0, m - src.rows, 0, n - src.cols, BORDER_CONSTANT, Scalar::all(0));    // 创建频域图像容器 (复数)    Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};    Mat complexImg;    merge(planes, 2, complexImg);    // 进行傅里叶变换 (DFT)    dft(complexImg, complexImg);    // 创建拉普拉斯滤波器    Mat laplacianFilter = createLaplacianFilter(complexImg.size());    // 应用拉普拉斯滤波器    Mat resultFreq;    mulSpectrums(complexImg, laplacianFilter, resultFreq, 0);    // 计算处理后频域图像的幅度谱 (用于可视化)    Mat magProcessed;    Mat processedPlanes[2];    split(resultFreq, processedPlanes);    magnitude(processedPlanes[0], processedPlanes[1], magProcessed);    magProcessed += Scalar::all(1); // 避免 log(0)    log(magProcessed, magProcessed);    normalize(magProcessed, magProcessed, 0, 1, NORM_MINMAX);    int cx_processed = magProcessed.cols / 2;    int cy_processed = magProcessed.rows / 2;    Mat q0_processed(magProcessed, Rect(0, 0, cx_processed, cy_processed));    Mat q1_processed(magProcessed, Rect(cx_processed, 0, cx_processed, cy_processed));    Mat q2_processed(magProcessed, Rect(0, cy_processed, cx_processed, cy_processed));    Mat q3_processed(magProcessed, Rect(cx_processed, cy_processed, cx_processed, cy_processed));    Mat tmp_processed;    q0_processed.copyTo(tmp_processed);    q3_processed.copyTo(q0_processed);    tmp_processed.copyTo(q3_processed);    q1_processed.copyTo(tmp_processed);    q2_processed.copyTo(q1_processed);    tmp_processed.copyTo(q2_processed);    // 进行傅里叶逆变换    Mat resultImg;    idft(resultFreq, resultImg, DFT_REAL_OUTPUT);    // 裁剪回原始图像大小    resultImg = resultImg(Rect(0, 0, src.cols, src.rows));    // 归一化到 0-255 并转换为 CV_8U    Mat resultImg_normalized;    normalize(resultImg, resultImg_normalized, 0, 255, NORM_MINMAX, CV_8U);    // 锐化    addWeighted(src, alpha, resultImg_normalized, (1 - alpha), 0, dst);    return magProcessed;}int main() {    Mat src = imread(".../girl.jpg");    if (src.empty()) {        cout << "无法加载图像" << endl;        return -1;    }    // 通道分离    vector<Mat> channels;    split(src, channels);    // 对每个通道应用频域拉普拉斯锐化并获取处理后频域图像的幅度谱    double alpha = 1.5; // 锐化强度因子    Mat sharpenedB, sharpenedG, sharpenedR;    Mat processedMagB = processChannelAndGetProcessedMag(channels[0], sharpenedB, alpha);    Mat processedMagG = processChannelAndGetProcessedMag(channels[1], sharpenedG, alpha);    Mat processedMagR = processChannelAndGetProcessedMag(channels[2], sharpenedR, alpha);    // 合并锐化后的通道    vector<Mat> sharpenedChannels = {sharpenedB, sharpenedG, sharpenedR};    Mat sharpenedImg;    merge(sharpenedChannels, sharpenedImg);    imshow("src", src);    imshow("Processed Magnitude Spectrum (Blue Channel)", processedMagB);    imshow("Processed Magnitude Spectrum (Green Channel)", processedMagG);    imshow("Processed Magnitude Spectrum (Red Channel)", processedMagR);    imshow("Sharpened Color Image", sharpenedImg);    waitKey(0);    return 0;}

频域拉普拉斯响应(即拉普拉斯滤波器作用后的图像)G(u,v)=H(u,v)F(u,v)G(u,v)=H(u,v)⋅F(u,v)

频域拉普拉斯响应通常不适合直接显示:

    拉普拉斯响应具有正负值对称性(不是图像强度)
    值域动态范围极大,不适合线性可视化

频域中经过拉普拉斯滤波后,结果的值可能在很大的范围内波动(甚至数万级别),远远超出 [0, 255]。直接转换为 8 位图像会导致:

    响应是中间量,本身不代表直观图像内容。

所以,例子中展示了幅度谱,它是对频域响应的一种可视化方式。

4. 总结

在频域中,拉普拉斯算子通过傅里叶变换将空间域的微分操作转换为频域的乘法操作。主要用于增强图像高频成分以突出边缘和细节,典型应用包括图像锐化(通过逆变换叠加原图与拉普拉斯响应)、边缘检测(抑制低频保留高频突变信号)、频域滤波器设计(结合高低通滤波实现带通或带阻效果)以及图像恢复中的正则化约束(抑制噪声)。它是图像增强与分析的非常有用的工具。

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

拉普拉斯算子 傅里叶变换 图像锐化 OpenCV 频域分析
相关文章