天天看点

opencv 同态滤波实现 homofilter

同态滤波的原理很简单,详细介绍请自行search,本文主要专注于opencv下homofilter的实现,并给出代码和运行结果,此前的参考了一些网上opencv的homofilter实现代码,然并卵啊,花了点时间自己做。

算法基本流程如下:

1. 原始输入图像: 通常是亮度分量,Y分量,色度分量不用处理

2. ln:对数变化,原理上是说将乘性信号分解为加信信号,具体原理自己搜一下

3. 变换到频域:dft,fft,dct都行,我用的是dct,dct变换后的结果是实数,节省空间,低频分量集中在左上角,高频分量集中在右下角

opencv 同态滤波实现 homofilter

4. high pass filter:关键部分,homofilter观点认为光照不均匀的部分集中在低频分量,将低频分量滤除可以消去光照不均的影响,从而提升暗处和高亮处物体的可见度,但整体亮度变化区间会变小。具体滤波器的设计不多讲了,这里值得注意的是dct结果的原点处的值是原始图像的能量总和,是不能直接滤除的,否则你得到的是一张黑色的图片,什么也看不见,因为总能量被减小后,图像的整理亮度下降。所以滤波器原点值设为1,这样保证滤波后的图片和原始图片能量一致,既平均亮度一致,如果你想提升整体亮度,也可以将滤波原点值设为大于1。

5. 频域变换到空域:idft, idct...

6. exp:指数运算,ln的反变换

其中3,4,5只是完成高通滤波,也可以直接在空域用滑动窗口滤波实现,尝试了7x7的gaussianblur,构建一个高通滤波,效果不好

代码如下:

void my_HomoFilter(Mat srcImg, Mat &dst)
{
    
    srcImg.convertTo(srcImg, CV_64FC1);
    dst.convertTo(dst, CV_64FC1);
    //1. ln
    for (int i = 0; i < srcImg.rows; i++)
    {
        double* srcdata = srcImg.ptr<double>(i);
        double* logdata = srcImg.ptr<double>(i);
        for (int j = 0; j < srcImg.cols; j++)
        {
            logdata[j] = log(srcdata[j]+0.0001);
        }
    }
    
    //spectrum
    //2. dct
    Mat mat_dct = Mat::zeros(srcImg.rows, srcImg.cols, CV_64FC1);
    dct(srcImg, mat_dct);
    imshow("dct", mat_dct);
    
    //3. linear filter
    Mat H_u_v;
    double gammaH = 1.5;
    double gammaL = 0.5;
    double C = 1;
    double d0 = (srcImg.rows/2)*(srcImg.rows/2) + (srcImg.cols/2)*(srcImg.cols/2);
    double d2 = 0;
    H_u_v = Mat::zeros(srcImg.rows, srcImg.cols, CV_64FC1);
    
    double totalWeight = 0.0;
    for (int i = 0; i < srcImg.rows; i++)
    {
        double * dataH_u_v = H_u_v.ptr<double>(i);
        for (int j = 0; j < srcImg.cols; j++)
        {
            d2 = pow((i), 2.0) + pow((j), 2.0);
            dataH_u_v[j] = 	(gammaH - gammaL)*(1 - exp(-C*d2/d0)) + gammaL;
            totalWeight += dataH_u_v[j];
        }
    }
    H_u_v.ptr<double>(0)[0] = 1.1;
    
    //H_u_v = Mat::ones(srcImg.rows, srcImg.cols, CV_64FC1);
    imshow("H_u_v", H_u_v);


    //imshow("before filter", mat_dct);

    mat_dct = mat_dct.mul(H_u_v);
    //Mat tmp = mat_dct.mul(H_u_v);
    //tmp.copyTo(mat_dct);
    //4. idct
    idct(mat_dct, dst);
    
#if 0
    //spatial high high pass filter
    Mat tmp = Mat::zeros(srcImg.rows, srcImg.cols, CV_64FC1);
    GaussianBlur(srcImg, tmp, Size(9, 9), 1.5, 1.5);
    const double alpha = 0.5;
    
    for (int i = 0; i < srcImg.rows; i++)
    {
        double* srcdata = srcImg.ptr<double>(i);
        double* blurdata = tmp.ptr<double>(i);
        double* dstdata = dst.ptr<double>(i);
        for (int j = 0; j < srcImg.cols; j++)
        {
            dstdata[j] = (1+alpha)*srcdata[j] - alpha*blurdata[j];
            //dstdata[j] = blurdata[j];
            
        }
    }
     
    
    
#endif
    //5. exp
    for (int i = 0; i < srcImg.rows; i++)
    {
        double* srcdata = dst.ptr<double>(i);
        double* dstdata = dst.ptr<double>(i);
        for (int j = 0; j < srcImg.cols; j++)
        {
            dstdata[j] = exp(srcdata[j]);
        }
    }
    
    //imshow("dst", dst);
    dst.convertTo(dst, CV_8UC1);

}
           

主程序代码:

Mat src_mat = imread("/Users/lilingyu1/Documents/video_enhance/data/dark_1136x1136.jpg", 0);
    imshow("src:", src_mat);
    Mat dst_mat(src_mat.rows, src_mat.cols, src_mat.type());
    my_HomoFilter(src_mat, dst_mat);
   
    
    imshow("dst:", dst_mat);
    cvWaitKey(0);
           

运行结果如下,可以看出整体亮度范围变小,暗处的细节显现,缺点是涉及频域变换,计算复杂,经过滤波不可避免会丢失细节

opencv 同态滤波实现 homofilter
opencv 同态滤波实现 homofilter