1차원 바코드 인식은 이미지에서 바코드 영역 전체를 분리하는 과정이 없이도 처리가 가능하다. 이미지의 한 스캔라인이 바코드 영역에 걸쳐있기만 해도 인식하는데 충분하기 때문이다. 스캔라인에서 바코드 정보를 뽑아내기 위해서는 이진화 과정을 거쳐야 하는데 이 또한 adaptive 한 방식으로 처리할 수 있다. 바코드 영역은 전경과 배경이 매우 균일하게 섞여 있으므로 적당한 너비의 스캔라인 구간(moving window)에서 픽셀 평균값을 기준으로 임계값을 정해도 충분하다. 아래의 코드는 일정한 크기의 moving window를 이용해서 바코드를 담고 있는 영상을 스캔라인 별로 이진화를 시킨다. 윈도가 한 픽셀 이동하면 이전 평균값을 빼고, 새로운 픽셀 값을 더해서 윈도 평균을 업데이트한다. 스캔라인 시작 부분에서는 윈도 평균값 정보가 없으므로 이전 스캔라인의 평균값을 사용한다. 이 알고리즘은 이미지를 한 번만 스캔하고도 이진화가 가능해서 연산 비용이 매우 저렴한 알고리즘이다(바코드를 발견한 스캔라인에서 종료시키면 이미지를 다 처리할 필요도 없다). 그리고 윈도 크기를 이미지 폭으로 하더라도 여전히 스캔라인 별로 달라지는 adaptive 방식이다.  처음 몇 개의 스캔라인이 바코드와 겹치는 영역이 아니면 윈도 평균값 계산이 제대로 이루어지지 않으므로 잘못 이진화될 수 있지만 바코드 영역에 들어서면 정상적으로 동작하게 된다. 적용 예를 보면 시작 라인이 (비트맵의 시작 라인은 맨 아래이다) 바코드를 포함하지 않으므로 잘못 이진화가 되는 것을 볼 수 있다. 글씨가 전 영역에 거의 균일하게 인쇄된 이미지의 이진화에도 잘 동작하여 OCR에도 응용할 수 있다.

void MovingAvgThreshold(BYTE *image, int width, int height, int wsz, BYTE *res) {
    if (wsz < 0 || wsz > width) wsz = width / 4; // default window size;
    double sum = 128 * wsz;                   // initial moving window sum = 128 * wsz;
    double sumOld = sum;                      // backup sum of the first wsz pixels in each row;
    for (int y = 0, pos = 0; y < height; y++) {           
        sum = sumOld;                         // reset sum = result of previous row;
        for (int x = 0; x < wsz; x++) {
            int v = image[pos];
            sum += v - sum / wsz;                // update sum;
            res[pos++] = v < (sum / wsz) ? 0: 0xFF;
        }
        sumOld = sum;                            // backup for next line;
        for (int x = wsz; x < width; x++) {
            int v = image[pos];
            sum += v - sum / wsz;                // update sum;
            res[pos++] = v < (sum / wsz) ? 0: 0xFF;			
        }
    }
}

728x90
Posted by helloktk
,