OpenCV 同态滤波 高斯高通 C++

概述

https://blog.csdn.net/cyf15238622067/article/details/86657370 文中介绍的入射分量和反射分量模型,可开发一种频域处理过程,通过压缩灰度范围和增强对比度来改善一幅图像的外观。

频谱域同态滤波

图像可以表示为照度和反射率的乘积:

 f(x,y) = i(x,y)r(x,y) 

现在定义:

z(x,y)=ln^{f(x,y)}=ln^{i(x,y)}+ln^{r(x,y)}

则有:

\Im \left \{ z\left ( x,y \right ) \right \}=\Im \left \{ln ^{f(x,y))} \right \}=\Im \left \{ ln^{i(x,y))} \right \}+\Im \left \{ ln^{r(x,y))} \right \}

Z\left ( u,v\right ) = F_{i}(u,v)+F_{r}(u,v)

式中 F_{i}(u,v) F_{r}(u,v)分别是 ln^{i(x,y)} ln^{r(x,y)}的傅里叶变换。

然后用H(u,v)对Z(u,v)滤波

S(u,v)=H(u,v)Z(u,v) = H(u,v)F_{i}(u,v) + H(u,v)F_{r}(u,v)

在空间域中:

s(x,y)=\Im ^{-1}\left \{ S(u,v) \right \}=\Im ^{-1}\left \{ H(u,v)F_{i}(u,v) \right \}+\Im ^{-1}\left \{ H(u,v)F_{r}(u,v) \right \}

由定义:

i^{'}(x,y)=\Im _{-1}\left \{ H(u,v) F_{i}(u,v)\right \}

r^{'}(x,y)=\Im _{-1}\left \{ H(u,v) F_{r}(u,v)\right \}

可以用下列表达式来表达:

s(x,y)=i^{'}(x,y)+r^{'(x,y)}

最后因为z(x,y)是通过取输入图像的自然对数形成的,可以通过取滤波后结果的指数反处理输出图像。

g(x,y)=e^{s(x,y)}=e^{i^{'}(x,y)}e^{r^{'}(x,y)}=i_{0}(x,y)r_{0}(x,y)

其中 i0是图像的照射, r0是图像的反射成分。

刚刚推导的滤波方法总结如下图:

该方法的关键在于入射分量和反射分量的分离;然后对其分别进行滤波操作;

图像的入射分量通常由慢的空间变化来表征;而反射分量往往引起突变,特别是在不同物体的连接部分。

这些特性导致图像取对数后傅里叶变换低频成分与照射联系,而高频部分与反射联系。虽然联系是粗略的近似,但他们用在图像滤波中是有益的。

使用同态滤波器可以更好的控制入射分量和反射分量。这种控制需要指定一个滤波器函数H(u,v);可用不同的方法影响傅里叶变换的高频和低频。下图是滤波器的剖面图

   
如果rH ,rL选定,而rL<1,rH>1;那么滤波器函数趋向于衰减低频(照射)的贡献,增强高频(反射)的贡献。最终结果是同时进行动态范围的压缩和对比度的增强。

上述剖面图可用高斯的变换形式表示:

H(u,v)=(\gamma _{H}-\gamma _{L})\left [ 1-e^{-c\left [ D^{2}(u,v)/D_{0}^{2} \right ]} \right ]+\gamma _{L}

D(u,v)是频率域中心(u,v)与频率矩形中心的距离。

常数c控制函数边坡的锐利度,它在rL 和rH之间过度。

例子:全身PET扫描图像,有点模糊,支配动态显示范围的高灰度“热点”使得低灰度特征很朦胧。

使用高斯同态滤波处理,令 rL=0.25,rH=2,c=1,D0=80

代码实现:

#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;


cv::Mat image_add_border( cv::Mat &src )
{
    int w=2*src.cols;
    int h=2*src.rows;
    std::cout << "src: " << src.cols << "*" << src.rows << std::endl;

    cv::Mat padded;
    copyMakeBorder( src, padded, 0, h-src.rows, 0, w-src.cols,
                    cv::BORDER_CONSTANT, cv::Scalar::all(0));
//    padded.convertTo(padded,CV_32FC1);
    std::cout << "opt: " << padded.cols << "*" << padded.rows << std::endl;
    return padded;
}

//transform to center 中心化
void center_transform( cv::Mat &src )
{
    for(int i=0; i<src.rows; i++){
        float *p = src.ptr<float>(i);
        for(int j=0; j<src.cols; j++){
            p[j] = p[j] * pow(-1, i+j);
        }
    }
}

//对角线交换内容
void zero_to_center(cv::Mat &freq_plane)
{
//    freq_plane = freq_plane(Rect(0, 0, freq_plane.cols & -2, freq_plane.rows & -2));
    //这里为什么&上-2具体查看opencv文档
    //其实是为了把行和列变成偶数 -2的二进制是11111111.......10 最后一位是0
    int cx=freq_plane.cols/2;int cy=freq_plane.rows/2;//以下的操作是移动图像  (零频移到中心)
    cv::Mat part1_r(freq_plane, cv::Rect(0,0,cx,cy));  //元素坐标表示为(cx,cy)
    cv::Mat part2_r(freq_plane, cv::Rect(cx,0,cx,cy));
    cv::Mat part3_r(freq_plane, cv::Rect(0,cy,cx,cy));
    cv::Mat part4_r(freq_plane, cv::Rect(cx,cy,cx,cy));

    cv::Mat tmp;
    part1_r.copyTo(tmp);  //左上与右下交换位置(实部)
    part4_r.copyTo(part1_r);
    tmp.copyTo(part4_r);

    part2_r.copyTo(tmp);  //右上与左下交换位置(实部)
    part3_r.copyTo(part2_r);
    tmp.copyTo(part3_r);
}


void show_spectrum( cv::Mat &complexI )
{
    cv::Mat temp[] = {cv::Mat::zeros(complexI.size(),CV_32FC1),
                      cv::Mat::zeros(complexI.size(),CV_32FC1)};
    //显示频谱图
    cv::split(complexI, temp);
    cv::Mat aa;
    cv::magnitude(temp[0], temp[1], aa);
//    zero_to_center(aa);
    cv::divide(aa, aa.cols*aa.rows, aa);
    cv::imshow("src_img_spectrum",aa);
}

//频率域滤波
cv::Mat frequency_filter(cv::Mat &padded,cv::Mat &blur)
{
    cv::Mat plane[]={padded, cv::Mat::zeros(padded.size(), CV_32FC1)};
    cv::Mat complexIm;

    cv::merge(plane,2,complexIm);
    cv::dft(complexIm,complexIm);//fourior transform
    show_spectrum(complexIm);

    cv::Mat dst_plane[2];
    cv::multiply(complexIm, blur, complexIm);
//    mulSpectrums(complexIm, blur, complexIm, 0);
    cv::idft(complexIm, complexIm, DFT_INVERSE);       //idft

    cv::split(complexIm, dst_plane);

//    cv::magnitude(dst_plane[0],dst_plane[1],dst_plane[0]);  //求幅值(模)
    center_transform(dst_plane[0]);        //center transform

    return dst_plane[0];
}

//高斯同态滤波器
cv::Mat gaussian_homo_kernel( cv::Mat &scr, float rh, float rl, float c, float D0   )
{
    cv::Mat gaussian_high_pass(scr.size(),CV_32FC2);
    int row_num = scr.rows;
    int col_num = scr.cols;
    float r = rh -rl;
    float d0 = 2 * D0 * D0;
    for(int i=0; i<row_num; i++ ){
        float *p = gaussian_high_pass.ptr<float>(i);
        for(int j=0; j<col_num; j++ ){
            float d = pow((i - row_num/2),2) + pow((j - col_num/2),2);
            p[2*j]   = r*(1 - expf(-1*c*(d/d0))) + rl;
            p[2*j+1] = r*(1 - expf(-1*c*(d/d0))) + rl;
        }
    }

    cv::Mat temp[] = { cv::Mat::zeros(scr.size(), CV_32FC1),
                       cv::Mat::zeros(scr.size(), CV_32FC1) };
    cv::split(gaussian_high_pass, temp);
    std::string name = "滤波器d0=" + std::to_string(D0);
    cv::Mat show;
    cv::normalize(temp[0], show, 1, 0, CV_MINMAX);
    cv::imshow(name, show);
    return gaussian_high_pass;
}

Mat homofilter( Mat image_in, float rh, float rl, float c, float D0  )
{
    image_in.convertTo(image_in,CV_32FC1);
    log(image_in+1, image_in);
    Mat padded = image_add_border(image_in);
    center_transform(padded);

    Mat blur = gaussian_homo_kernel(padded, rh, rl, c, D0 );
    Mat dst = frequency_filter(padded, blur);
    cv::normalize(dst, dst, 5, 0, CV_MINMAX);       //归一化5-0,因为e^6 = 387 e^5 = 143,避免溢出

    exp(dst,dst);
    dst = dst - 1;

    return dst;

}

int main() {
    Mat src = imread("462.tif", 0);
    resize(src, src, Size(), 0.5, 0.5);
    imshow("原图像", src);
    Mat dst = homofilter(src,2, 0.25, 1, 80 );
    dst = dst(Rect(0,0,src.cols, src.rows));

    cv::normalize(dst, dst, 255, 0, CV_MINMAX);
        dst.convertTo(dst, CV_8U);

    imshow("同态滤波", dst);
    waitKey(0);
}

猜你喜欢

转载自blog.csdn.net/cyf15238622067/article/details/88420836