OpenCV 笔记(38):同态滤波

85 阅读5分钟

1. 同态滤波

同态滤波(Homomorphic Filtering)是一种经典的图像增强方法,主要用于同时校正图像的 非均匀光照增强细节对比度

同态滤波的核心思想是将图像的光照分量(低频)和反射分量(高频)分离,并分别进行调整,最终改善图像的对比度和细节。

2. 算法流程

2.1 图像模型

图像可表示为光照分量i(x,y)i(x,y)和反射分量r(x,y)r(x,y)的乘积:f(x,y)=i(x,y)r(x,y)f(x,y)=i(x,y)⋅r(x,y)

其中:

  • i(x,y)i(x,y):入射光照分量(illumination),通常变化平缓,包含低频信息。
  • r(x,y)r(x,y):物体的反射分量(reflectance),变化剧烈,包含高频信息(细节、边缘)。
  • f(x,y)f(x,y):观测到的图像。

2.2 对数变换

为了能够独立处理光照和反射,同态滤波的第一步是对图像的像素值取对数。因为在数学上,乘法运算在取对数后会变成加法运算。

z(x,y)=lnf(x,y)=lni(x,y)+lnr(x,y)z(x,y) = lnf(x,y)=lni(x,y)+lnr(x,y)

此后,光照分量和反射分量在频域中更易于分离。

2.3 傅里叶变换

对取对数变换后的图像z(x,y)z(x,y)进行傅里叶变换,转换到频域,得到频谱Z(u,v)Z(u,v)

Z(u,v)=F{z(x,y)}Z(u, v)=\mathcal{F}\{z(x, y)\}

这样做的目的是将图像分解为不同频率的成分。

2.4 设计滤波器

设计一个合适的滤波器函数H(u,v)H(u,v),并在频域中将频谱 Z(u,v)Z(u,v) 与该滤波器相乘,得到滤波后的频谱 S(u,v)S(u,v)

S(u,v)=H(u,v)Z(u,v)S(u,v)=H(u,v)⋅Z(u,v)

根据频率成分的特性进行增强或衰减。一个典型的同态滤波器会衰减低频成分(减少光照不均),并增强高频成分(增强细节和对比度)。

因此,滤波器函数 H(u,v)H(u,v) 的设计是同态滤波的关键,不同的滤波器会产生不同的增强效果。

常见的同态滤波器形式包括:

  • 高斯型同态滤波器: 增强高频,衰减低频。过渡平滑,无振铃效应。
  • 巴特沃斯型同态滤波器: 具有更平滑的过渡带。阶数可调,灵活性高。
  • 指数型同态滤波器: 过渡特性介于高斯和巴特沃斯之间。
  • 理想高通同态滤波器: 低频完全抑制,高频完全保留。会导致严重振铃效应,实际应用较少。

这里,我们以高斯型同态滤波器为例,它是一种基于高斯函数设计的频域滤波器,结合同态滤波框架与高斯函数的频域特性,对图像的光照分量(低频)和反射分量(高频)进行差异化调整。我们用以下公式来表示高斯同态滤波器的传递函数:

H(u,v)=(γHγL)[1ecD2(u,v)D02]+γLH(u, v)=\left(\gamma_{H}-\gamma_{L}\right)\left[1-e^{-c \frac{D^{2}(u, v)}{D_{0}^{2}}}\right]+\gamma_{L}

其中,

  • γL\gamma_{L}: 低频增益(通常 <1,抑制光照不均)。
  • γH\gamma_{H}: 高频增益(通常 >1,增强细节)。
  • c : 控制滤波器陡峭程度的常数。
  • D0D_0: 截止频率。
  • D(u,v)D(u,v):频率点到中心的距离。

这个形式是一种 高斯型高通滤波器,嵌入到同态滤波的增益控制中,用来调节低频(光照分量)和高频(细节分量)的权重。

2.5 傅里叶逆变换

将滤波后的频谱 S(u,v)S(u,v) 转换回空间域,得到滤波后的对数图像 s(x,y)s(x,y)

s(x,y)=F1{S(u,v)}s(x, y)=\mathcal{F}^{-1}\{S(u, v)\}

2.6 指数运算

对图像 s(x,y)s(x,y) 的每个像素值进行指数运算,得到最终的增强图像 g(x,y)g(x,y)

g(x,y)=exp(s(x,y))g(x,y)=\exp(s(x,y))

这个步骤是为了恢复原始图像的尺度和亮度范围。由于第一步进行了对数运算,这里需要进行指数运算来还原。

总结一下整个算法的流程

原始图像 → 对数变换 → 傅里叶变换 → 同态滤波 → 逆傅里叶变换 → 指数变换 → 输出图像

3. 使用 OpenCV C++ 实现同态滤波

下面的例子,展示了如何基于上述的算法流程构建单通道同态滤波,以及如何构建高斯型高通同态滤波器。对于彩色图像而言,需要先分离出 B、G、R 通道,每个通道单独使用同态滤波,最后再合并回彩色图像。

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

// 构造高斯型高通同态滤波器
Mat createHighPassFilter(Size size, float gammaH, float gammaL, float c, float D0) {
    Mat H(size, CV_32F);
    Point center = Point(size.width / 2, size.height / 2);
    for (int u = 0; u < size.height; u++) {
        for (int v = 0; v < size.width; v++) {
            float D = sqrt(pow(u - center.y, 2) + pow(v - center.x, 2));
            H.at<float>(u, v) = (gammaH - gammaL) * (1.0f - exp(-c * (D * D) / (D0 * D0))) + gammaL;
        }
    }
    return H;
}

// 单通道同态滤波
Mat homomorphicFilter(const Mat& image, float gammaH, float gammaL, float c, float D0) {
    Mat floatImg;
    image.convertTo(floatImg, CV_32F);    // 转换为 float 类型,方便后续计算
    floatImg += 1;                      // 防止 log(0) 出现无穷大
    log(floatImg, floatImg);            // 对图像取对数:ln(f(x, y))

    // 对图像扩展到最佳 DFT 尺寸
    int m = cv::getOptimalDFTSize(image.rows);
    int n = cv::getOptimalDFTSize(image.cols);
    Mat padded;
    copyMakeBorder(floatImg, padded, 0, m - image.rows, 0, n - image.cols, BORDER_CONSTANT, Scalar::all(0));

    // 创建复数矩阵,实部为 padded,虚部为 0
    Mat planes[] = { padded, Mat::zeros(padded.size(), CV_32F) };
    Mat complexI;
    merge(planes, 2, complexI);

    // 进行傅里叶变换
    dft(complexI, complexI);

    // 创建高通同态滤波器,尺寸和 padded 一致
    Mat H = createHighPassFilter(padded.size(), gammaH, gammaL, c, D0);

    // 将频谱从笛卡尔坐标转换为极坐标(幅值和相位)
    split(complexI, planes);
    Mat mag, phase;
    cartToPolar(planes[0], planes[1], mag, phase);

    // 在频率域应用滤波器(只对幅值部分)
    mag = mag.mul(H);

    // 将极坐标转换回笛卡尔坐标
    polarToCart(mag, phase, planes[0], planes[1]);
    merge(planes, 2, complexI);

    // 傅里叶逆变换,回到空域
    idft(complexI, complexI);

    // 提取实部,去掉填充部分
    split(complexI, planes);
    Mat realI = planes[0](Rect(0, 0, image.cols, image.rows));

    // 归一化到 [0, 10],避免指数溢出
    normalize(realI, realI, 0, 10, NORM_MINMAX);

    // 对结果取指数,还原到线性空间
    Mat img_out;
    exp(realI, img_out);

    // 最后归一化到 [0, 255] 并转换为 8 位图像
    double minVal, maxVal;
    minMaxLoc(img_out, &minVal, &maxVal);
    img_out.convertTo(img_out, CV_8U, 255.0 / (maxVal - minVal), -minVal * 255.0 / (maxVal - minVal));

    return img_out;
}

int main() {
    Mat src = imread(".../landscape.jpg", IMREAD_UNCHANGED);
    if (src.empty()) {
        cout << "无法加载图像" << endl;
        return -1;
    }

    Mat result;

    if (src.channels() == 1) {
        // 灰度图像处理
        result = homomorphicFilter(src, 1.5f, 0.5f, 1.0f, 30.0f);
    } else if (src.channels() == 3) {
        // 彩色图像处理(逐通道)
        vector<Mat> channels;
        split(src, channels);
        vector<Mat> resultChannels(3);

        for (int i = 0; i < 3; ++i) {
            resultChannels[i] = homomorphicFilter(channels[i], 1.5f, 0.5f, 1.0f, 30.0f);
        }
        merge(resultChannels, result);
    } else {
        cout << "不支持的图像通道数" << endl;
        return -1;
    }

    imshow("src", src);
    imshow("result", result);
    waitKey(0);

    return 0;
}

原图.png

同态滤波后的效果图.png

4. 总结

同态滤波是一种通过频域处理解决图像光照不均的关键技术,其作用在于分离并差异化调整图像的低频光照分量和高频反射分量,增强图像对比度和细节,从而提高图像的视觉质量和可分析性。这在光照条件复杂或需要突出局部特征的应用中具有重要意义。