양의 정수 $x$가 주어질 때, 이보다 크거나 같은 가장 작은 $2^n$으로 표현되는 숫자를 찾아보자. 왜? FFT 때문이다. 물론, $\tt n = int(ceil(\log(double(x)) / \log(2.)))$로 계산하거나, 

                    int a = 1; 
                    while (a < x) a <<= 1; 

로 찾을 수 있다;

$2^{n-1} < x \le  2^n$ 사이에 있으면, 원하는 답은 $2^n$이다. $x=2^n$인 경우를 제외하고 모두 $n$번째 비트가 1로 세팅이 된다. 따라서, 통일시키기 위해서 1을 빼면, $x-1$ 은 $n$ 번째 비트가 항상 1로 주어진다.  이제, 남은 일은 $n-1$ 번째에서 0번째까지 모든 비트를 1로 채우면 된다. 그 결과에 1을 더하면 원하는 $2^n$을 얻는다.  $n$-번째 비트가 1이고 이를 이용해서 하위 비트를 채우기 위해서는 차례로 >> 연산을 한 후에 or 연산을 하면 된다.

a = x - 1
a                   = 1xxxxxxxxxxxxxxxxxxxxxxxx
a >> 1             = 01xxxxxxxxxxxxxxxxxxxxxxx
a >> 2             = 001xxxxxxxxxxxxxxxxxxxxxx 
.......

31번의 >> 연산을 한 후에 or 연산을 하면 $n$ 이하의 모든 비트가 1로 채워진다(31은 최대인 경우다. 사전에 답을 모르므로 >>를 31번까지 해야 한다). 이것은 너무 낭비다.  잘 살펴보면,

a | (a>>1) = 11xxxxxxxxxxxxxxxxxxxxxxxxx

형태여서, 상위 두 자리 비트가 이미 세팅이 되어 있으므로, 이 결과를 다시  >> 2 하여 이전 결과와 or 연산을 하면 상위 4자리의 비트가 채워지고, 또다시 이 결과를 >>4 하고 난 후에 직전 결과와 or 연산을 하면 상위 8자리의 비트가 1로 채워진다. 이런 식으로 하면 >>16까지 하면 32자리를 모두 커버할 수 있다.

따라서, 전체적인 알고리즘은 $(x > 0)$

   x-- ; 
   x |= (x >> 1);  //상위 2 자리 채움
   x |= (x >> 2);  //상위 4자리 채움 
   x |= (x >> 4);  //상위 8자리 채움 
   x |= (x >> 8);  //상위 16자리 채움 
   x |= (x >> 16);//상위 32자리 채움 
   x++;
   return x;

단, 32비트 머신에서만이다. 64비트 프로그래밍에서는 >>32 도 추가로 해주어야 한다.

728x90

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

Ellipse Parameters  (0) 2012.10.13
float 타입 변수의 절대값은?  (0) 2012.02.17
Is Pow of 2  (0) 2012.02.13
Fixed-point RGB2Gray  (0) 2012.01.25
Otsu-알고리즘의 새로운 해석  (0) 2010.01.28
Posted by helloktk
,

FFT를 적용할 때 이미지의 폭이나 높이가 2의 지수승으로 주어지는 경우가 가장 간단하다. 따라서 주어진 정수 x가 2의 지수 승인가 판별할 수 있는 방법이 필요하다. x가 양의 정수이고, 2^n으로 표현이 된다면, 2진수로 나타낼 때, (n+1) 번째 비트만 1이고 (0부터 센다), 나머지 비트는 모두 0이다. 그리고 x-1은  n 번째에서 0번째까지 모든 비트가 1이 된
다.  
    x                    x(2진수)            x-1
    1                      1                        0
    2                    10                         1
    4                   100                       11
    8                  1000                     111
     ................................................................
이 표를 보면, x와 x-1 사이에는 겹치는 비트가 없다. 따라서 두 수를 and 연산을 하면 0 이 되는 경우에는 2의 지수승이고, 그 이외의 경우에는 0이 아님을 알 수 있다. x = 2의 지수승  판별은    

                           return  x & (x - 1) == 0         /* x != 0 인 정수*/

인가를 보면 된다.

그런데 이 판별식은 x = 0 인 경우에는 성립이 안된다. x-1 = -1 이므로 32비트 자리 전부가 1로 채워지므로 x & (x-1) = 0 이어서 2의 지수승으로 판별한다. 따라서 0을 제외하는 방법을 찾아야 한다. (물론 함수 인자에서 양수로 제한을 하면 되지만 폼이 안 난다). 음수는 최상위 비트가 1로 채워진다는 사실을 이용하자. 최상위 비트를 1로 만들려면,

~0U                    =111111111111111..11111111(32개) 
~0U>>1              = 011111111111111..11111111
~(~0U>>1)          =100000000000000..00000000 
~(~0U>>1)|x       = x의 최상위 비트를 항상 1로 채워준다(음수 일 때는 자동으로 만족)
                             나머지 비트는 그대로 둔다.

따라서 이 값과 x-1을 and 연산을 하면 0 이하인 수가 들어오면 연산이 결과를 항상 0이 아니게 된다.

                          return  !((~(~0U>>1)|x) & (x - 1))          /*x = 정수 */    

0 하나를 예외 처리하기 위해서 너무 많은 과정을 거치는 것이 아닌가? 

728x90

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

float 타입 변수의 절대값은?  (0) 2012.02.17
x 보다 크거나 같은 가장 작은 2^n ?  (0) 2012.02.13
Fixed-point RGB2Gray  (0) 2012.01.25
Otsu-알고리즘의 새로운 해석  (0) 2010.01.28
Object Orientation  (1) 2010.01.17
Posted by helloktk
,

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

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

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

2차원 바코드가 아닌 일차원 바코드 영상을 이진화할 때는 이만큼 복잡한(?) 알고리즘을 쓸 필요가 없다. 일차원 바코드는 보통 한 scanline의 정보만으로도 인식이 가능하므로 라인 단위의 이진화를 시키면 충분히다. 이 경우도 moving average를 사용하면 매우 간단하게 adaptive 한 임계값을 구할 수 있다. scanline 기준이므로 integral image는 따로 필요하지 않다.

void makeIntegralImage(BYTE *image, int width, int height, int* intImage);
더보기
void makeIntegralImage(BYTE *image, int width, int height, int* intImage) {    
    intImage[0] = image[0]; 
    for (int x = 1; x < width; ++x)
        intImage[x] = intImage[x - 1] + image[x];
    //next line;
    image += width;
    for (int y = 1, offset = y * width; y < height; ++y, offset += width) {
        int linesum = 0;
        for(int x = 0; x < width; ++x) {
            linesum += image[x];
            intImage[offset + x] = intImage[offset - width + x] + linesum ;
        }
        //next line;
        image += width ;
    }
}
/*
** 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;
        }
    }
}

 

QR 코드가 인쇄된 지면에 그라데이션이 있어서 전역 이진화로는 코드의 분리가 쉽지 않다.
728x90

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

Least Squares Estimation of Perspective Transformation  (4) 2012.02.15
Perspective Transformation  (2) 2012.02.14
Peak Finder  (1) 2012.02.02
QR-code: decoder  (0) 2012.01.26
QR-code: detector  (0) 2012.01.12
Posted by helloktk
,

Peak Finder

Image Recognition 2012. 2. 2. 21:41

주어진 데이터를 받아서 극대점과 극소점을 찾는다. 극점은 이전 극점과 일정한 차이(delta)가 날 때만 취한다. 맨 처음과 나중에 들어오는 데이터가 극점일 때, 별도의 처리가 필요하다.

BOOL peakFinder(std::vector<double>& data, double delta, /*IN*/
                std::vector<int>& maxPosition, /*OUT*/ 
                std::vector<int>& minPosition) /*OUT*/ {
    int maxPos = -1, minPos = -1;
    bool lookForMax = true;
    double maxVal = -FLT_MAX, minVal = FLT_MAX ;
    for (int i = 0; i < data.size(); i++) {
        double currVal = data[i] ;
        if (currVal > maxVal) {
            maxVal = currVal ;
            maxPos = i;
        } else if (currVal < minVal) {
            minVal = currVal ;
            minPos = i;
        }
        if (lookForMax) {
            // 극대값을 찾는 상태이고, 현재값이 maxVal보다 충분이 아래면,
            // 이 maxVal이 극대값임 & 현재위치가 극소값의 후보가 됨;
            if (currVal < (maxVal - delta)) {
                maxPosition.push_back(maxPos);
                minVal = currVal ;
                minPos = i;
                lookForMax = false; //다음번에는 최소값을 찾는다;
            }
        } else {
            // 극소값을 찾는 상태이고, 현재값이 minVal보다 충분히 위면,
            // 이 minVal이 극소점임 & 현재위치가 극대값 후보가 됨;
            if (currVal > (minVal + delta)) {
                minPosition.push_back(minPos);
                maxVal = currVal;
                maxPos = i;
                lookForMax = true; //다음번에는 최대값을 찾는다;
            }
        }
    }
    return TRUE;
};

데이터 구간이 (0,255) 히스트그램:

delta=5
delta=10
delta=15

 

 
728x90

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

Perspective Transformation  (2) 2012.02.14
Integral Image을 이용한 Adaptive Threshold  (0) 2012.02.04
QR-code: decoder  (0) 2012.01.26
QR-code: detector  (0) 2012.01.12
Adaboost  (0) 2010.12.28
Posted by helloktk
,

QR-code: decoder

Image Recognition 2012. 1. 26. 00:54

이미지에서 코드의 영역을 검출한 후에 는 코드 영역의 비트 값을 읽어 들여서 디코딩하는 과정을 거쳐야 최종적으로 QR-code가 담고 있는 정보를 읽을 수 있다. 검출된 코드 영역은 역 perspective 변환을 거치면 정사각형의 코드 영역으로 변환된다. 그러나 이 과정은 실제 프로그램 설계에서는 불필요한 과정이다. 이미 주어진 perspective 변환을 이용하면, 이진화된 원래의 이미지에서 바로 코드를 재구성할 수 있다. 아래의 그림은 이 과정을 빠르게 수행하기 위해서 코드 영역을 블록 단위로 구분하는 마스크 이미지를 만든 것을 보여준다.

 

 

마스크 이미지: 컬러는 각 블록의 라벨링을 표현하기 위해서 사용했다.

 

이 마스크를 이용하면 각 블럭의 비트 값을 majority test에 의해서 쉽게 결정할 수 있다.
재구성된 코드블럭:

 


이제 나머지는 QR-code의 스펙을 참고하면 (Reed-Solomon error-correction code는 영상처리 관점에서 약간 벗어나므로 취급하지 않음) 인코딩 된 정보를 얻을 수 있다:

 

 

 

 

예제 코드의 정보는

 

 

728x90

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

Integral Image을 이용한 Adaptive Threshold  (0) 2012.02.04
Peak Finder  (1) 2012.02.02
QR-code: detector  (0) 2012.01.12
Adaboost  (0) 2010.12.28
Blur Detection  (0) 2010.05.25
Posted by helloktk
,