Computer Vision

중간값 필터 미디언 필터(Median Filter)

익플루 2014. 7. 8. 12:02
반응형

오늘은 노이즈 제거(잡 제거) 중에

미디언 필터(중간값 필터)를 사용하여 영상을 향상시키는 방법에 대해 알아봅시다.

 

 

중간값 필터(median filter)는 비선형 디지털 필터 기술로 이미지나 기타 신호로부터 신호 잡음을 제거하는 데 자주 이용되며, 통상적으로 이미지 프로세싱에서 윤곽선 감지 같은 높은 수준의 처리를 수행하기 전 단계인 이미지에 고성능 잡음 제거를 수행하는 데 필요하다.

중간값 필터는 이미지 프로세싱에서 자주 이용되며, 특히 스펙클 노이즈나 작은 반점들을 줄이는 데 유용하다. 중간값 필터의 윤곽선 보호 성질은 원치 않는 윤곽선 번짐이 있는 경우에 유용하다.

- 위키백과

 

라고 합니다.

그럼 중간값 필터의 원리를 보면은,

 

  아래 그림[1]과 같은 농도를 가진 영상이 있다고 가정하자. 그림에서 3x3의 영역내(굵은 선)의 9개의 화소를 오름차순으로 정렬하면 다음과 같다.

그림 [1] 일반적인 화소값을 구하는 예

 

   이 때의 중앙 값(이것을 median이라 한다), 이 경우는 전부 9개이기 때문에, 왼쪽에서 5번째의 농도4가 구할 수 있는 화소의 농도가 된다. 위의 예에서 10이라고 하는 원래의 화소는 잡음 성분이 었는데, 확실히 제거 되었다. ( 가운데 부분에 10이 4로 중간 크기의 값으로 저장된다)

이것은 주변과 비교하여 극단적으로 농도의 차이가 있는 것은 크기의 순서로 나열했을 때, 왼쪽이나 오른쪽으로 치우치게 되어 중앙값으로 선택되지 않기 때문이다.

이와 같이 미디언 필터(Median Filter)는 어떤 화소 주변의 영역내의 화소 농도의 중간값을 구하여 원하는 화소의 농도로 처리하는 것이다.

그러면, 다음의 오른쪽 화소(농단값 3)를 계산 해보면 중앙값은 실제로는 3인데 4가 되고 만다. 이 경우에는 Median Filter의 오류에 속하지만 시각적으로 보기에는 잘 구분할 수 없다. 그 보다 중요한 것은 에지(edge)부분을 보존할 수 있느냐 하는 것이다. 다음의 예를 살펴보자.

그림 [2] 에지 부분에서 화소를 구하는 예

 

  위의 그림 [2]에서 왼쪽부분은 에지가 있는 화소지만, 굵은 선으로 표시된 화소들의 순서를 구해보면, 오른쪽과 같이 되어 완전하게 에지가 보존됨을 알 수 있다.  Mean Filter의 경우 잡음 성분도 평균의 계산에 포함되므로 출력이 잡음의 영향을 받지만 Median Filter의 경우는 잡음 성분이 선택되는 경우가 드물므로 출력에 그다지 영향을 주지 않는다.

 

이 두가지 기법에 의한 결과를 살펴보면, 미디언 필터를 사용한 경우가 훨씬 선명한 출력 데이터를 가짐을 알 수 있다. 하지만 Median Filter의 경우는 정렬을 하기 때문에 시간상으로 Mean Filter보다 5배정도 걸린다는 단점이 있다. 프로그래을 적용한 영상을 살펴보면 다음과 같다.

 

                       

             (a) 입력 noise 영상                       (b) Median filter 적용 후의 영상 

 

http://vision.incheon.ac.kr/ipa/ipa07.php

 

 

자 그럼. 이제 구현을 한번 해봅시다

 

 

#include <iostream>
#include <fstream>

 using namespace std;


// 클래스 선언
class IMAGE {
private:
 int Width;
 int Height;
 unsigned char *Original;
 unsigned char *Result;
 int *temp;

// 사용할 함수 선언
public:
 IMAGE(int _Width = 256, int _Height = 256);
 ~IMAGE();
 void ReadImage(char *);
 void WriteImage(char *);
 void Median();
 int Align(int [], int);
 void Satur();
};

// 클래스 생성
IMAGE::IMAGE(int _Width , int _Height )
{
 Width = _Width;
 Height = _Height;
 Original = new unsigned char [Width*Height];
 Result = new unsigned char [Width*Height];
 temp = new int [Width*Height];
}
//클래스 소멸
IMAGE::~IMAGE()
{
 delete []Original;
 delete []Result;
 delete []temp;
}

// ***** Main함수 시작 ***** //
void main()
{
 IMAGE image;

 char in_name[20];

 cout << "Digital Image Processing - Median Filter\n" << endl;
 cout << "Enter the input image name : ";
 cin >> in_name;

 image.ReadImage(in_name);
 image.Median();
 image.WriteImage("RESULT.RAW");
}

// 입력 영상 읽기
void IMAGE::ReadImage(char *fileName)
{
 ifstream inputFile;
 inputFile.open(fileName, ios::binary);

 if (inputFile.fail()) {
  cout << "Error! Input File not Exist." << endl;
  exit(1);
 }

 inputFile.read((char *)Original, Width*Height);
 inputFile.close();
}

// 결과 영상 저장
void IMAGE::WriteImage(char *fileName)
{
 ofstream outputFile;
 outputFile.open(fileName, ios::binary);

 outputFile.write((const char *)Result, Width*Height);
 outputFile.close();
}


// 미디언 필터 함수
void IMAGE::Median()
{
 int row, col;
 const int mskSz = 3;        // 여기서는 마스크를 3 x 3으로 하였습니다.
 int Num[mskSz];
 int NumCenter = mskSz/2;

 for(row=0; row<Height; row++) {
  for(col=0; col<Width; col++) {
   for (int n=0; n<mskSz; n++) {
    Num[n] = Original[row*Width+col+(n-NumCenter)];
   }
   temp[row*Width+col] = Align(Num, mskSz);  // Align()함수 중간값 계산하는 함수
  }
 }

 Satur(); //영상저장하는 함수
}

// 버블 정렬 함수  ( 3x3 마스크 안에 있는 수들을 정렬하기 위하여 )
int IMAGE::Align(int N[], int nSize)
{
 int hold;
 int k;
 k = nSize/2;   // 마스크의 중앙 위치 계산

 for (int loop=1; loop<nSize; loop++) {
  for (int i=0; i<nSize-1; i++) {
   if (N[i] > N[i+1]) {
    hold = N[i];
    N[i] = N[i+1];
    N[i+1] = hold;
   }
  }
 }

 return N[k];   // 중앙값을 반환
}


// 결과 영상 데이터 저장 (표현 범위를 0~255로 제약)
void IMAGE::Satur()
{
 int row, col;

 for (row=0; row<Height; row++) {
  for (col=0; col<Width; col++) {
  
   if (temp[row*Width+col] < 0)
    Result[row*Width+col] = 0;
  
   else if (temp[row*Width+col] > 255)
    Result[row*Width+col] = 255;
  
   else
    Result[row*Width+col] = temp[row*Width+col];
  }
 }
}

 

 

 

자 이 프로그램을 실행시키면

INPUT 이미지를 적으라는 cmd창이 뜨는데 같은 프로그램 안에 있는 파일 이름을 쓰고 엔터를 치면 미디언 필터링이 적용이 된다.

 

결과를 봅시다.

 

 

SMILL.RAWRESULT.RAW

왼쪽 (실행 전 ) 오른쪽 (실행 후)

 

 

AVE256.RAWRESULT.RAW

 왼쪽 (실행 전 ) 오른쪽 (실행 후)

 

 

실험 영상에 잡음이 많지 않아서 육안으로 보기에 많은 차이가 있어보이지 않지만

만약 salt같은 잡음이 있었다면 해결!!

 

자 이제 미디언 필터를 이용한 잡음제거는 까먹지 맙시다.

반응형