본문 바로가기
Programmer/openCV for Android

Android openCV #5 - BoxFilter 적용하기 (Filter 기본설명포함)

by JaehwanPark 2017. 4. 16.

Android openCV #5 - BoxFilter 적용하기 (Filter 기본설명포함)



아래내용은 제가 Gitbook으로 작성한 내용 입니다.

gitbook을 통해 바로 보실 수 도 있습니다!


그리고 포스팅 내용에 관한 문의는 언제든 환영입니다!!


https://jaehwanpark.gitbooks.io/opencv-android-study/content/boxfilter.html


Box Filter

첫 번째로 다뤄볼 Filter는 openCV 의 BoxFilter 입니다.

void cv::boxFilter(InputArray     src,
OutputArray     dst,
int     ddepth,
Size     ksize,
Point     anchor = Point(-1,-1),
bool     normalize = true,
int     borderType = BORDER_DEFAULT 
)

src : 입력영상
dst : 출력영상
ddepth : 출력영상의 depth
ksize : kernel size ( filter size)
anchor : filter의 중심점? Filter를 적용할 위치를 선정 (보통 pixel의 가운데를 지정함)

anchor에 대해서 조금 더 자세히

아래의 그림을 보면 확실하게 알수 있는데요, anchor 의 위치는 kernel (filter)에서 어느곳을 중심으로 filter를 적용시킬지에 대해서 세팅하는 것 입니다. 보통의 필터는 가운데 지점에 사용합니다.

BoxFilter를 적용하면 어떤 결과가 나오는지?

우선 이미지로 확인해보기 전에 행렬의 값을 통해 Boxfilter가 어떻게 값을 적용시키는지 확인해보겠습니다.

<<기본 조건>>

     uchar dataA[]={1, 2, 4, 5, 2, 1,
                   3, 6, 6, 9, 0, 3,
                   1, 8, 3, 7, 2, 5,
                   2, 9, 8, 9, 9, 1,
                   3, 9, 8, 8, 7, 2,
                   4, 9, 9, 9, 9, 3};

    Mat A(6,6,CV_8U,dataA);
    int border = 1; // 경계의 사이즈
    Size ksize(border*2 +1,border*2+1); //ksize(3,3)
    Point anchor(-1,-1); //filter를 적용할 지점?, -1,-1이면 Filter의 가운데로 설정한다.

코드
boxFilter(A,dst1,-1,ksize,anchor,false); // normalize false
수행결과
[ 35,  37,  53,  41,  32,  11;
  37,  34,  50,  38,  34,  17;
  52,  46,  65,  53,  45,  31;
  58,  51,  69,  61,  50,  44;
  63,  61,  78,  76,  57,  56;
  64,  62,  77,  73,  55,  53]
설명

우선 Filter 적용을 위해 Border 라는 것을 알아야 하는데요 (기본상식인건지.. 전 여기서부터 좀 막히더라구요)

저 난해한 값이 어떻게 나온건지.. 쉽게 알아보겠습니다^^

우선 Filter를 적용시키려면 경계에 있는 값들을 어떻게 할것인가? 에 대해서 고민해야 하는데요, 여기서는 border의 값을 1로 설정하였습니다. 왜냐하면 Filter의 크기가 3*3 이기 때문입니다.

위의 그림을 보면 알 수 있는데요, Mat A의 (0,0)에 Filter를 적용한다고 할 때, 0,0에서의 윗쪽,왼쪽에 Filter를 적용할 공간이 없게 됩니다. 그래서 border를 통해 Filter를 적용할 영상의 부족한 부분을 채워주게 되는데요, 부족한 부분을 어떤 값으로 채울지는 여러가지 방법이 있습니다. BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT, BORDER_REFLECT101, BORDER_WRAP 과 같은 방식이 있습니다.

자세히보시면 boxFilter의 기본 border type는 이미 BORDER_DEFAULT로 설정되어 있습니다. 그런데 이 BORD_DEFAULT는 BORDER_REFLECT101 과 같은 값입니다.

그럼 BORDER가 적용된 Mat A는 아래와 같은 모습이 됩니다.

자 이제 Filter를 적용할 준비가 되었네요, 여기서 Filter를 적용해 보면 아래와 같은 모습이 됩니다.


코드
boxFilter(A,dst2,-1,ksize,anchor,true);
수행결과
[  4,   4,   6,   5,   4,   1;
   4,   4,   6,   4,   4,   2;
   6,   5,   7,   6,   5,   3;
   6,   6,   8,   7,   6,   5;
   7,   7,   9,   8,   6,   6;
   7,   7,   9,   8,   6,   6]
설명

위의 예제와의 차이점은 normalize를 수행하느냐 안하느냐 여부 입니다.

원래 행렬의 값을 보면 최소 0 ~ 9의 값이 사용되었던 것을 알 수 있습니다. 하지만 Filter를 적용해서 최대 78이라는 값이 나오도록 기존에 있던 값과 차이가 많이 발생하게 되었습니다.

그래서 원래 이미지(행렬)과 유사한 범위를 표현하도록 normalize 를 수행하게 됩니다.
normalize 될 최대값은 기존 행렬의 값중 최대값 *9 /9가 될 것입니다. (즉 9*9 =81 /9)
위의 예제에서 [0.0] 의 값이 normalize 수행전에는 35였는데요, 이 값을 35/9 = 3.888 (소수점 반올림)을 통해 4가 됩니다.


아래는 사용한 코드 입니다.

JNIEXPORT jstring JNICALL
Java_com_tistory_technote_opencvandroid_MainActivity_convertNativeLibtoBoxFilter(JNIEnv *env, jobject, jlong addrInput, jlong addrResult) {

    Mat &img_input = *(Mat *) addrInput;
    Mat &img_result = *(Mat *) addrResult;
    cvtColor(img_input, img_result, CV_RGBA2GRAY);
    jstring result;
    std::stringstream buffer;

    uchar dataA[]={1, 2, 4, 5, 2, 1,
                   3, 6, 6, 9, 0, 3,
                   1, 8, 3, 7, 2, 5,
                   2, 9, 8, 9, 9, 1,
                   3, 9, 8, 8, 7, 2,
                   4, 9, 9, 9, 9, 3};

    Mat A(6,6,CV_8U,dataA);
    buffer << "A= " <<endl;
    buffer << A << endl;

    int border = 1;
    Mat B;
    copyMakeBorder(A,B,border,border,border,border,BORDER_REFLECT101);
    buffer << "B = " << endl;
    buffer << B << endl;

    Size ksize(border*2 +1,border*2+1); //ksize(3,3)
    Point anchor(0,0);
    Mat dst1;
    boxFilter(A,dst1,-1,ksize,anchor,false);

    buffer << "dst1 = " << endl;
    buffer << dst1 << endl;

    Mat dst2;
    boxFilter(A,dst2,-1,ksize,anchor,true);
    buffer << "dst2 = " << endl;
    buffer << dst2 << endl;

    Mat dst3;
    int d =ksize.width;
    double sigmaColor = 2.0;
    double sigmaSpace = 2.0;
    bilateralFilter(A,dst3,3,d,sigmaColor,sigmaSpace);
    buffer << "dst3 = " << endl;
    buffer << dst3 << endl;

    Mat dst4;
    bilateralFilter(A,dst4,3,-1,sigmaColor,sigmaSpace);
    buffer << "dst4 = " <<endl;
    buffer << dst4 << endl;

    const char *cstr = buffer.str().c_str();
    result = env->NewStringUTF(cstr);

    return result;
}


댓글