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);
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章