1차원 바코드는 이미지에서 바코드 영역 전체를 먼저 분리하지 않아도 인식이 가능하다. 이미지의 하나 이상의 스캔라인이 바코드를 충분히 가로지르기만 하면, 해당 스캔라인으로부터 바코드 정보를 추출하여 인식할 수 있기 때문이다.
스캔라인에서 바코드 정보를 얻기 위해서는 먼저 흑백 이진화가 필요하다. 바코드 영역에서는 조명의 변화가 막대와 배경의 폭에 비해 비교적 완만하므로, 스캔라인을 따라 일정한 크기의 moving window를 이동시키면서 계산한 국소 평균 밝기를 임계값으로 사용하는 adaptive thresholding 방법을 적용하면 안정적으로 이진화를 수행할 수 있다.
아래의 알고리즘은 각 스캔라인을 한 번만 순차적으로 스캔하면서 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;
}
}
}


'Image Recognition' 카테고리의 다른 글
| Multilevel Otsu Thresholding (0) | 2021.01.09 |
|---|---|
| Kuwahara Filter (2) | 2020.12.28 |
| Expectation Maximization Algorithm for Two-Component Gaussian Mixture (0) | 2017.01.02 |
| Union-Find Connected Component Labeling (0) | 2012.11.01 |
| RANSAC: Ellipse Fitting (1) | 2012.10.07 |

