Kuwahara Filter

Image Recognition 2020. 12. 28. 18:57
728x90

주어진 픽셀을 한 코너로 하는 4개 block 중에서 분산이 가장 작은 블록의 평균으로 중앙 픽셀 값을 대체하는 비선형 filter다. 이 필터를 적용하면  균일한 영역에서  spike noise 픽셀은 주변의 영역의 픽셀 값으로 대체되므로 제거될 수 있고, edge 라인을 기준으로 수직방향으로는 거의 균일한 영역이므로 필터링을 하더라도 edge가 뭉개지지 않음을 알 수 있다. 즉, Kuwahara filters는 이미지의 노이즈를 제거하고 edge를(위치) 보존하는 특성을 가진다.  윈도의 크기가 5x5인 경우 4개의 3x3영역에서 평균과 분산을 계산해서 중앙 픽셀의 값을 결정한다. integral 이미지를 사용하면 연산 부담을 줄일 수 있다.

원본 이미지
7x7 필터 적용 결과

이 Kuwahara filter보다도 보다 향상된 filter는 symmetric nearest neighbour (SNN) filter가 있다. 이 filter는 기준 픽셀에서 대칭 지점에 있는 두 지점의 픽셀 값 중에서 중앙 픽셀 값에 가까운 것을 취해서 평균값을 취한다.

 

아래 코드를 컬러 이미지에 적용하기 위해서는 RGB 채널별로 따로 적용하면 된다. 경계 영역은 추가적인 처리가 필요하다.

#define GET_SUM(x,y)  sum[ (y) * width + (x)]
#define GET_SUM2(x,y) sum2[(y) * width + (x)]
#define BLK_SUM(x,y) ((GET_SUM(x,y) + GET_SUM(x-hs1,y-hs1) \
                        - GET_SUM(x-hs1,y) - GET_SUM(x,y-hs1)))
#define BLK_SUM2(x,y) ((GET_SUM2(x,y) + GET_SUM2(x-hs1,y-hs1) \
                        - GET_SUM2(x-hs1,y) - GET_SUM2(x,y-hs1)))
void integral_image(BYTE *image, int width, int height, int *sum);
void integral_image_sq(BYTE *image, int width, int height, int *sum2);
void kuwahara_filter ( BYTE *image, int width, int height, int size, BYTE *out ) {
    int *sum = new int [width * height] ;
    int *sum2 = new int [width * height];
    // integral image; for window mean;
    integral_image(image, width, height, sum);
    // integral image of square; for window variance;
    integral_image_sq(image, width, height, sum2);
    
    // do filtering;
    size = ((size >> 1) << 1) + 1; // make window size be an odd number;
    int offset = ( size - 1 ) >> 1;
    int hs1 = ( size + 1 ) >> 1;
    int nn = hs1 * hs1;    // # of pixels within the  window;
    int var[4], mean[4];
    for (int y = hs1; y < height - hs1; ++y) {
        for (int x = hs1; x < width - hs1; ++x) {
            mean[0] = BLK_SUM(x, y) / nn;
            mean[1] = BLK_SUM(x + offset,          y) / nn;
            mean[2] = BLK_SUM(x,          y + offset) / nn;
            mean[3] = BLK_SUM(x + offset, y + offset) / nn;
            var[0] = BLK_SUM2(x,                   y) / nn - mean[0] * mean[0];
            var[1] = BLK_SUM2(x + offset,          y) / nn - mean[1] * mean[1];
            var[2] = BLK_SUM2(x,          y + offset) / nn - mean[2] * mean[2];
            var[3] = BLK_SUM2(x + offset, y + offset) / nn - mean[3] * mean[3];
            int vmin = var[0], id = 0;
            for (int i = 1; i < 4; ++i)
                if (var[i] < vmin) {
                    vmin = var[i]; id = i;
                }
            int a = mean[id]; 
            out[y * width + x] = a > 255 ? 255: a < 0 ? 0: a;
        }
    }
    delete[] sum;
    delete[] sum2;
};
Posted by helloktk

댓글을 달아 주세요

  1. hgmhc 2020.12.28 23:50 신고  댓글주소  수정/삭제  댓글쓰기

    이를 이용하면, 용량은 낮추되 원본과 비슷한 형태로 보존이 가능한 것인가요?