Box Filter - openCV
첫 번째로 다뤄볼 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;
}
관련문의
- 이메일: [email protected]
- Blog : technote.tistory.com
- Github : https://gitlab.com/Technote/opencv320-android-study