OpenCV模糊集合實現圖像增強 C++

模糊集合原理

       在計算機編程的時候,常常會使用一種“乾脆的”集合。在判斷某件事,或者某個變量的時候,常常使用的是布爾值(因爲某件事,不是真就是假)。通過一個閾值,去判斷這件事,而這樣的一個閾值的設定,會產生一個問題。

下圖

                                

       當一個人的年齡超過20歲,那麼這個人就不再屬於年輕人範疇。這樣來說,未免有些太過“殘忍”,畢竟,20多歲的人還是“比較”年輕的。這裏就出現了一個模糊的定義,“比較”年輕,這個集合既不屬於年輕,也不屬於非年輕,也就是其實年輕與非年輕之間的過度不應該是乾脆的,而應該是漸進的過度。

       定義Z爲對象集,其中,z表示Z中的一類元素(比如z表示年齡)。Z中的一個模糊集合A主要由一個隸屬度(Degree of membership)來表示。對此,模糊集合A是一個由z值和隸屬度函數組成的集合,即

                                                             

        當的時候,所有的z是模糊集合A的完全成員;當的時候,所有的z都不是模糊集合A的成員,當的值介於0和1之間,那麼此時的z稱爲模糊集合A的不完全成員。

       下面,還有幾個重要的性質。

       對於所有的,模糊集合A的補集(NOT),其隸屬度函數如下所示。

                                                            

       對於所有的,模糊集合A與模糊集合B的並集(OR)U,其隸屬度函數如下所示。

                                                         

       對於所有的,模糊集合A與模糊集合B的交集(AND)I,其隸屬度函數如下所示。

                                                   

       到這裏,其實已經可以用模糊集合來做一些事情了。對於一個問題的處理,在使用模糊集合來解決的時候,我們可以參考以下步驟。首先,需要將輸入量折算爲隸屬度,這個過程叫做“模糊化”。然後,使用得到的隸屬度來進行計算,或者判斷,或者其他更復雜的算法。最後,需要將隸屬度再次折算爲輸出,這個過程稱爲“去模糊”或者“反模糊”。

        模糊集合爲處理不嚴密信息提供了一種形式。將模糊與模糊隸屬度函數之間的關聯變量稱爲fuzification;通常用IF-THEN規則描述有關問題的知識。下面我們尋找一種方法,使用輸入和問題的先驗知識建立模糊系統的輸出。

   通常應用實現步驟爲:

 (1)模糊輸入:將IF-THEN規則描述的先驗知識映射到[0,1]區間,找到每個標量對應的模糊值。

 (2)執行任何需要的模糊邏輯操作(AND、OR、NOT),將所有輸出組合到一起;利用一種推斷方法,每個規則得到單一輸出。

 (3)應用聚合方法將所有規則的輸出組合到一起,得到單個的輸出模糊集合。

 (4)對最後的輸出去模糊化,得到“乾脆”標量輸出。(可以求模糊集的重心)

一、使用模糊集合進行灰度變換

        使用模糊集合來進行灰度變換,從而增強圖像。首先可以在常理下考慮一下,一般的對於動態範圍較小的圖像,我們一般的處理的方法是灰度拉昇,或者直方圖均衡。

         這兩種的方法的本質就是,讓原圖較暗的像素更加暗,讓原圖較亮的像素更加亮。那麼,我們規定如下模糊規則

                           R1:IF 一個像素是暗的,THEN 讓這個像素更暗;

                           R2:IF 一個像素是灰的,THEN 讓他保持是灰的;

                           R3:IF 一個像素是亮的,THEN 讓這個像素更亮;

        這個規則就代表了我們的處理方法。當然,IF條件中的像素是暗的(或者灰的,或者是亮的),這個概念都是模糊的。同理THEN結論中的更暗(或者保持灰的,或者更亮)亦是模糊的。爲此,我們需要確立一個隸屬度函數,從而來判斷一個像素對於三個條件的隸屬度。

        實際上,隸屬度函數的確定是很複雜的,然而,這裏我們則儘量想得簡單一點。首先,一個像素是暗的(模糊),那麼其隸屬度函數大致的形狀是,

    • 在低於某個值的時候域隸屬度爲1,
    • 在灰度越過某一個值之後,其隸屬度爲0,
    • 當然。然後之間進行線性插值,那麼,我們就可以得到R1的隸屬度函數了。同理,R2與R3也是一樣的。

        爲了簡單起見,我們將THEN結論中的更暗設置爲較爲簡單的函數。

    • 爲了讓這個像素更黑,其輸出都爲0。同理,
    • 爲了使這個像素保持灰的,我們將其輸出設爲0.5,
    • 爲了使得一個像素更亮,我們將其設置爲1。

        根據以上討論,我們所決定的隸屬度函數如下所示。

                             

使用輸入的隸屬度函數,可以得到模糊化後的數據。

        對於一個像素,需要根據規則R1,R2R3,計算出所對應的隸屬度,這個過程,稱之爲模糊化。將一個輸入量模糊化,所使用的函數(或者說是對應關係),稱之爲知識庫。 

         模糊化之後,得到一個像素所對應的三個隸屬度之後,就可以進行反模糊化了。反模糊化的算法很多,這裏使用簡單的重心法去進行計算。

                               

        公式中的Udark、Ugray、Ubright由對應的當前像素點,根據右邊曲線圖來計算獲得。 Vd = 0,表示全黑;Vg = 127, 表示中間灰度;Vb = 255,表示白。Vo表示生成的結果圖像當前像素值。         

        其中Vd,Vg,Vb爲輸出的單一值,對於一個像素,需要根據規則R1,R2與R3,計算出所對應的隸屬度。得到一個權值衡量後的成熟度估計值,最爲輸出值。到此,就得到了輸出,整個算法的效果如下圖所示


 

代碼實現:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <string>

#define throDark 30
#define throMid 100
#define throBright 170

cv::Mat equalize_img( cv::Mat &src_img )
{
    cv::Mat result;
    cv::equalizeHist( src_img, result );
    return result;
}

cv::Mat show_img_histogram( cv::Mat &src_img )
{
    //create 256 subinterval
    //the number of possibles values
    int numbins = 256;

    //set the range for BGR(0-256)
    float range[] = { 0, 256};
    const float* histRange = { range };

    cv::Mat gray_hist;
    cv::calcHist( &src_img, 1, 0, cv::Mat(), gray_hist, 1, &numbins, &histRange );

    //draw histogram
    //draw lines for each channels
    int width = 512;
    int height = 300;

    // Create image with gray base
    cv::Mat histImage( height, width, CV_8UC1, cv::Scalar(20, 20, 20) );

    // Normalize the histograms to height of image
    cv::normalize(gray_hist, gray_hist, 0, height, cv::NORM_MINMAX );

    int binStep= cvRound((float)width/(float)numbins);
    for( int i=1; i< numbins; i++){
        cv::line(
            histImage,
            cv::Point( binStep*(i-1), height-cvRound(gray_hist.at<float>(i-1) ) ),
            cv::Point( binStep*(i), height-cvRound(gray_hist.at<float>(i) ) ),
            cv::Scalar(255,0,0)
            );
    }
    return histImage;
}

double getUdark(double num){
    double tmpUdark;
    if(num <= throDark){
        tmpUdark = 1;
    }else if((num > throDark) && (num <= throMid)){
        tmpUdark  = ((double)(throMid - num)) / ((double)(throMid - throDark));
    }else{
        tmpUdark = 0;
    }
    return tmpUdark;
}

double getUmid(double num){
    double tmpUmid;
    if((num > throDark) && (num < throMid)){
        tmpUmid  = (num - throDark) / (throMid - throDark);
    }else if((num >= throMid) && (num < throBright)){
        tmpUmid  = (throBright - num) / (throBright - throMid);
    }
    else{
        tmpUmid = 0;
    }
    return tmpUmid;
}

double getUbright(double num){
    double tmpUbright;
    if(num <= throMid){
        tmpUbright = 0;
    }else if((num > throMid) && (num <= throBright)){
        tmpUbright  = (num - throMid) / (throBright - throMid);
    }else{
        tmpUbright = 1;
    }
    return tmpUbright;
}

cv::Mat fuzzy_deal( cv::Mat &srcImage )
{
    if (srcImage.empty())
        std::cout << "No data" <<std::endl;

    cv::Mat resultImage = cv::Mat::zeros(srcImage.size(), srcImage.type());
    uchar val = 0;
    double Udark, Umid, Ubright;
    for (int i = 0; i < srcImage.rows; i++){
        for (int j = 0; j < srcImage.cols; j++){
            val = srcImage.at<uchar>(i, j);
            Udark   = getUdark(val);
            Umid    = getUmid(val);
            Ubright = getUbright(val);
            val = (0 * Udark + 127 * Umid + 255 * Ubright) / (Udark + Umid + Ubright);
            resultImage.at<uchar>(i, j) = cv::saturate_cast<uchar>(val);
        }
    }
    return resultImage;
}

int main(int argc , char* argv[])
{
    if(argc < 2){
        std::cerr << "Please input picture!\n" << std::endl;
        return 0;
    }
    cv::Mat image;

    image = cv::imread(argv[1], cv::IMREAD_GRAYSCALE);
    cv::Mat hist1 = show_img_histogram(image);
    imshow( "Histogram1", hist1);
    imshow( "src", image);

    cv::Mat equail = equalize_img(image);
    cv::Mat his = show_img_histogram(equail);
    imshow( "Histogram_deal", his);
    imshow( "Histogram2", equail);

    cv::Mat dst_img = fuzzy_deal(image);
    cv::Mat hist2 = show_img_histogram(dst_img);
    imshow( "Histogram3", hist2);
    imshow("Blur_deal", dst_img);
    cv::waitKey(0);
    return 0;
}

 

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