Adaptive threshold 방법을 적용하는 데 있어서 윈도우 계산의 로드를 줄이는 방법은 integral image을 이용하면 된다(물론 메모리의 소요가 부가적으로 발생하지만, 현재의 기기에서는 메모리는 별로 문제가 안된다).

아래의 코드는 integral 이미지를 이용해서 주어진 moving 윈도우 내의 픽셀합을 구하고 그것의 평균 (= local average)을 기준으로 영상을 이진화 시키는 함수다 (정확히는 "평균값-3" 이다. 여기서 3은 바코드 인식 공개라이브러리인 zbar에서 쓰는 기준을 잡았다. zbar 라이브러리에서는 moving average를 구하여 임계값으로 사용하는데, 윈도우가 이동하면서 나가는 픽셀과 들어오는 픽셀을 업데이트 하는 과정이  이 라이브러리에서는 정확히 구현이 되어있지는 않다. 그러나, 근사적으로는 맞게 구현되어 있으므로 코드는 대부분의 경우에 원하는데로 잘 동작을 한다. 적분영상을 이용하면, 윈도우가 이동하면서 픽셀정보를 업데이트 하는 복잡한 과정이 필요없이 적분영상의 단순 합/차만 수행하면 된다)

"윈도우 평균-3" 대신 보다 합리적인 기준을 잡으려면, 보통은 윈도우의 표준편차을 이용할 수 있다. 그러나 이 경우에는 합의 제곱에 대한 적분영상이 하나 더 필요하고, 얼마의 편차를 허용할 것인지를 정해주어야 한다. 이 기준에 맞게 구현된 코드는 http://kipl.tistory.com/30 에서 찾을 수 있다.

2차원 바코드가 아닌 일차원 바코들 영상을 이진화할 때는 이 만큼 복잡한(?) 알고리즘을 쓸 필요가 없다. 일차원 바코드는 한 스캔라인의 정보만으로도 보통 인식이 가능하므로 라인단위의 이진화면 충분히다. 이 경우에도 이동평균을 사용하면 매우 간단하게, 그리고 adaptive한 임계값을 구할수 있는데, 라인기준이므로 적분영상이 따로 필요하지 않다.

void makeIntegralImage(BYTE *image, int width, int height, int* intImage) {

더보기

/*
** moving window의 중심에 해당픽셀을 놓을 필요는 없다; 
*/
void thresholdByIntegralImage(BYTE *image, int width, int height, int wsz,
                              BYTE *matrix) { 
    std::vector<int> intImage(width * height);
    makeIntegralImage(image, width, height, &intImage[0]);
    const int winArea = wsz * wsz ;
    /*const int wsz = 10;*/
    for (int y = 0, offset = 0; y < height; y++, offset += width) {
        int top = y - (wsz>>1) ;
        if (top < 0 ) top = 0;
        else if (top > height - wsz) top = height - wsz;
        int bottom = top + wsz - 1;
        // y-range = [top, bottom];
        for (int x = 0; x < width; x++) {
            int left = x - (wsz>>1);
            if (left < 0) left = 0;
            else if (left > width - wsz) left = width - wsz;
            int right = left + wsz - 1;
            // xrange = [left, right];
            //
            int sum1 = (left > 0  && top > 0) ? intImage[(top - 1) * width + left - 1] : 0;
            int sum2 = (left > 0) ? intImage[bottom * width + left - 1] : 0;
            int sum3 = (top > 0) ? intImage[(top - 1) * width + right] : 0;
            //
            int graySum = intImage[bottom * width + right] - sum3 - sum2 + sum1;
            // overflow ? 
            // Threshold T = (window_mean - 3); why 3?
            if ((image[offset + x] + 3) * winArea <= graySum) {
                matrix[offset + x] = 0xFF; //inverted!
            } else {
                matrix[offset + x] = 0x00;
            }
        }
    }
}



저작자 표시 비영리 변경 금지
신고

'Image Recognition' 카테고리의 다른 글

Least Square Estimation of Perspective Transformation  (4) 2012.02.15
Perspective Transformation  (2) 2012.02.14
Integral Image을 이용한 Adaptive Threshold  (0) 2012.02.04
Peak Finder  (1) 2012.02.02
QR-code : decoder  (0) 2012.01.26
QR-code : detector  (0) 2012.01.12
Posted by helloktk