Just a Computer Graphics Studio & My Life

在了解雙邊濾波器 (Bilateral Filter)理論之後,來試驗程試跑出來的效果。比起其它影像模糊化 (Image Smoothing)的方法,雙邊濾波器執行時間較長模糊效果較佳!我所使用的筆電跑本程式每張圖約2.5分鐘

此程式分為三個檔案,需建立專案來編譯執行。環境建置可參考Dev-C++4.9.9.2 安裝 OpenCV 2.0

main.cpp
/**
	Theme: Bilateral Filter
	compiler: Dev C++ 4.9.9.2
	Library: OpenCV 2.0
	Date: 101/10/14
	Author: HappyMan
	Blog: https://cg2010studio.wordpress.com/
*/
#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
#include "BilateralFilter.h"

void displayImage(char * winName, IplImage* image){
	cvNamedWindow(winName, 1);
	cvShowImage(winName, image);
}

int main()
{
	char *filename = "night.jpg";
	IplImage *oimage = cvLoadImage(filename);

	displayImage("original", oimage);

	double spacestdv = 10;
	double rangestdv = 6;
	BilateralFilter *bilateral = new BilateralFilter(oimage, spacestdv, rangestdv);
	IplImage *bilateralImage = bilateral->runFilter();

	displayImage("bilateral", bilateralImage);
	cvSaveImage("bilateral.jpg", bilateralImage);

	cvWaitKey(0);
	return 0;
}
BilateralFilter.h
#include "cv.h"
#include "cxcore.h"

class BilateralFilter{
public:
        IplImage * image;
        IplImage * rimage ;

        double kernelRadius;
        double ** kernelD;
        double *gaussSimilarity;

        BilateralFilter(IplImage *image,double sigmaD, double sigmaR);
        IplImage * runFilter();
        void apply(int i,int j);
        bool isInsideBoundaries(int m,int n);
        double similarity(int p,int s);
        double gauss(double sigma,int x, int y);
        double BilateralFilter::getSpatialWeight(int m, int n,int i,int j);
};
BilateralFilter.cpp
#include "BilateralFilter.h"
#include <math.h>

int max(int num1, int num2){
    if(num1>num2)
        return num1;
    else
        return num2;
}
int getValue(IplImage * image,int i,int j){
    CvScalar pixel = cvGet2D(image,i,j);
    int pixelValue = pixel.val[0];
    return pixelValue;
}
BilateralFilter::BilateralFilter(IplImage *image,double sigmaD, double sigmaR){
    this->image = cvCloneImage(image);
    int sigmaMax = max(sigmaD, sigmaR);
    this->kernelRadius = ceil((double)2 * sigmaMax);

    double twoSigmaRSquared = 2 * sigmaR * sigmaR;

    int kernelSize = this->kernelRadius * 2 + 1;

    kernelD = new double*[kernelSize];
    for(int i=0; i<kernelSize;i++){
        kernelD[i] = new double[kernelSize];
    }
    int center = (kernelSize - 1) / 2;
    for(int x = -center; x < -center + kernelSize; x++){
        for (int y = -center; y < -center + kernelSize; y++){
            kernelD[x + center][y + center] = this->gauss(sigmaD, x, y);
        }
    }
    gaussSimilarity = new double[256];
    for(int i = 0; i < 256; i++){
        gaussSimilarity[i] = exp((double)-((i) / twoSigmaRSquared));
    }
    rimage = cvCloneImage(image);
}
	IplImage * BilateralFilter::runFilter(){
    for(int i=0;i<rimage->height;i++){
        for(int j=0;j<rimage->width;j++){
            apply(i,j);
        }
    }
    return rimage;
}
double BilateralFilter::getSpatialWeight(int m, int n,int i,int j){
    return kernelD[(int)(i-m + kernelRadius)][(int)(j-n + kernelRadius)];
}
void BilateralFilter::apply(int i, int j) {// ~i=y j=x
    if(i>0 && j>0 && i<image->height && j< image->width){
        double sum = 0;
        double totalWeight = 0;
        int intensityCenter = getValue(image,i,j);

        int mMax = i + kernelRadius;
        int nMax = j + kernelRadius;
        double weight;

        for (int m = i-kernelRadius; m < mMax; m++){
            for (int n = j-kernelRadius; n < nMax; n++){
                if (this->isInsideBoundaries(m, n)){
                    int intensityKernelPos = getValue(image,m,n);
                    weight = getSpatialWeight(m,n,i,j) * similarity(intensityKernelPos,intensityCenter);
                    totalWeight += weight;
                    sum += (weight * intensityKernelPos);
                }
            }
        }
        int newvalue=(int)floor(sum / totalWeight);

        CvScalar pixel;
        pixel.val[0]= newvalue;
        pixel.val[1]=newvalue;
        pixel.val[2]=newvalue;
        cvSet2D(rimage,i,j,pixel);
    }
}
double BilateralFilter::similarity(int p, int s){
    // this equals: Math.exp(-(( Math.abs(p-s)) /  2 * this->sigmaR * this->sigmaR));
    // but is precomputed to improve performance
    return this->gaussSimilarity[abs(p-s)];
}
double BilateralFilter::gauss (double sigma, int x, int y){
    return exp(-((x * x + y * y) / (2 * sigma * sigma)));
}
bool BilateralFilter::isInsideBoundaries(int m,int n){
    if(m>-1 && n>-1 && m<image->height && n <image->width)
        return true;
    else
        return false;
}

有兩個參數可作調整:

  • space
  • range

我使用宿舍頂樓美麗的夜景做為例子~

space = 10;color= 1

space = 10;color= 6

space = 10;color= 15

space = 1;color= 6

space = 20;color= 6

參考:bilateralfilter – A bilateral filter version written in C++【OpenCV】鄰域濾波:方框、高斯、中值、雙邊濾波

廣告

Comments on: "[OpenCV] 雙邊濾波器 (Bilateral Filter)" (6)

  1. 你好,關於這個程式我有個小問題,

    OpenCV bilateral filter function 的參數應該有三項 kernel size, sigmaD, sigmaC

    但是你所貼的 OpenCV 中的bilateral code 似乎user 不能決定 kernel size ?

    而是由 sigmaD, sigmaC 來決定,這樣的作法是有其根據嗎?

    喜歡

  2. 先生你好,

      感謝您對雙邊濾波器的清楚介紹,有了程式碼對照,讓人能更容易的了解雙邊濾波器的概念。想請教您一個問題:
      
      在BilateralFilter.cpp的第36行
    gaussSimilarity[i] = exp((double)-((i) / twoSigmaRSquared));
    ^
    這邊的i是否為i^2的筆誤呢?

    喜歡

發表留言

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s

標籤雲

%d 位部落客按了讚: