아래 그림을 보면 우리는 데이터를 연결하는 두 개의 직선을 생각할 수 있을 것이다.  그럼 두 개의 직선을 어떻게 얻을 것인가? 물론, ICA(independent component analysis)를 이용하는 것이 한 가지 방법이 될 것이다. 여기서는 EM 알고리즘을 이용하여서 두 개의 직선을 기술하는 기울기와 $y$-절편의 값을 구하는 방법을 알아보자.

 

 

사용자 삽입 이미지


직선을 각각 $y=a_1 x + b_1$, $y = a_2  x + b_2$라고 하면, $(a_1, b_1)$, $(a_2, b_2)$를 구하는 문제이다. 만약 각각의 data가 에러가 수반되는 측정에 의해서 얻어졌다고 하자. 에러 분포가 정규분포를 따른다면(여기서는 두 개의 모델 모두 갖은 표준편차를 갖는다고 가정했다) $i$-번째의 데이터가 각각의 직선 모델 1과 2에 속할 확률은 (posterior with equal prior) Bayes 공식에 의해서 

$$ w_1[i] = \frac{ e^{ - \frac{ r_1^2[i]}{2\sigma^2} } } {e^{ - \frac{ r_1^2[i]}{2\sigma^2} } + e^{ - \frac{ r_2^2[i]}{2\sigma^2} } }, \quad w_1[i] = \frac{ e^{ - \frac{ r_2^2[i]}{2\sigma^2} } } {e^{ - \frac{ r_1^2[i]}{2\sigma^2} } + e^{ - \frac{ r_2^2[i]}{2\sigma^2} } }, \quad i = 0,1,2,... $$

로 주어진다. 여기서 $r_1(i)$와 $r_2(i)$는 residual error이다:

$$r_1[i] = a_1 x[i] + b_1 - y[i],\quad r_2[i] = a_2 x[i] + b_2 - y[i], \quad i=0,1,2,...$$

(*이 값 대신에 직선까지 거리=$\frac{|a_k x + b_k - y|}{\sqrt{1+ a_k^2}},~ k=1,2)$로 대체해도 된다)

이제 각각의 데이터에 대해서 posterior를 구하였으므로(E-step) 이 값을 가중치로 하여서 직선의 방정식을 다시 갱신한다. 즉, 각각의 data 점들에 대한 $w_1(i)$를 가중치로 하여서 다시 직선 모델 1의 파라미터를 재계산하고, 동일한 과정을 $w_2(i)$를 가지고 직선 모델 2를 계산하여서 $(a_1, b_1)$, $(a_2, b_2)$를 재계산한다(M-step). 이 갱신된 파라미터를 이용하여서 다시 가중치를 구하는 작업과, 직선의 파라미터를 구하는 작업을 일정하게 수렴할 때까지 반복을 하는 과정을 수행한다.

아래의 그림은 3번 만에 원하는 결과를 얻는 것을 보여준다. 직선의 파라미터에 대한 초기값과 residual error의 표준편차 파라미터에 대한 적절한 값의 선택이 중요하다.

사용자 삽입 이미지

// 코드의 일부...
std::vector<CPoint> data ;                             // data,
std::vector<double> w1(data.size()), w2(data.size());  // weights.
double a1, b1, a2, b2 ;                                // line params;
double sigma ;
// E-step;
void calcWeights() {
    for (int i = data.size(); i-- > 0;) {
        double  x = data[i].x, y = data[i].y ;
        double r1 = a1 * x + b1 - y ;
        double r2 = a2 * x + b2 - y ;
        double n1 = SQR(r1) / SQR(sigma) / 2;
        double n2 = SQR(r2) / SQR(sigma) / 2;
        double p1 = exp( - n1);
        double p2 = exp( - n2);
        w1[i] = p1 / (p1 + p2);
        w2[i] = p2 / (p1 + p2);
    }
};
//  M-step
void estimModels() {
    double s1xx = 0, s1x = 0, s1 = 0, s1y = 0, s1xy = 0;
    double s2xx = 0, s2x = 0, s2 = 0, s2y = 0, s2xy = 0;
    for (int i = data.size(); i-- > 0;) {
        double  x = data[i].x,
                y = data[i].y;
            s1xx += w1[i] * SQR(x);
            s1xy += w1[i] * x * y;
            s1x  += w1[i] * x;
            s1y  += w1[i] * y;
            s1   += w1[i];
            //
            s2xx += w2[i] * SQR(x);
            s2xy += w2[i] * x * y;
            s2x  += w2[i] * x;
            s2y  += w2[i] * y;
            s2   += w2[i];
    };
    double det1 = s1xx * s1 - SQR(s1x);
    double det2 = s2xx * s2 - SQR(s2x);
    a1 = (s1 * s1xy  - s1x * s1y ) / det1;
    b1 = (s1xx * s1y - s1x * s1xy) / det1;
    a2 = (s2 * s2xy  - s2x * s2y ) / det2;
    b2 = (s2xx * s2y - s2x * s2xy) / det2;
}
 
728x90

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

Robust Line Fitting  (0) 2008.07.08
EM: Binarization  (0) 2008.07.01
Gaussian Mixture Model  (2) 2008.06.07
Rasterizing Voronoi Diagram  (0) 2008.05.26
RANSAC Algorithm  (0) 2008.05.24
,

영상에는 보통 우리가 관심이 있는 물체와 배경을 동시에 담고 있다. 그런데 어떤 영상의 경우 전체 픽셀 값의 다이내믹 레인지(dynamic range)가 매우 좁아서, 달리 이야기하면 영상의 히스토그램을 살펴볼 때 픽셀 값의 분포가 매우 좁은 영역에 한정이 되어있으면 물체와 배경의 구별이 잘 안되는 영상이 되게 된다. 이러한 영상의 경우 몇 가지 영상 처리 방법을 이용하면 좋은 contrast를 갖는 영상을 만들 수 있다.

Histogram Equalization(HE)은 이러한 기법 중의 하나로 좁은 영역에 분포하는 픽셀 값을 가능한 모든 범위에 골고루 분포하도록 재배치하여서 영상의 contrast를 증대시키는 방법이다. Histogram Equalization에서 사용하는 픽셀 값의 mapping은 

(1) 출력 영상의 픽셀 값 범위는 전 그레이 레벨이어야 하고 

(2) 각각의 그레이 레벨에 해당하는 픽셀의 수가 거의 일정하여야 한다: 픽셀 값이 이산적이어서 완벽하게 상수가 될 수는 없다, 좀 더 정확히는 출력 영상의 히스토그램 누적합이 그레이 레벨에 선형 비례해야 한다는 조건으로 결정된다.

입력 영상에서 값이 k 인 픽셀은 출력 영상에서 어떤 그레이 값을 가져야 할까? 조건에 따르면 입력 영상의 k 그레이 레벨이 출력 영상의 j 그레이 레벨로 매핑이 된다면, 출력 영상에서는 0부터 j까지 그레이 레벨에 해당하는 픽셀 수는 모든 그레이 레벨에서 픽셀 분포가 균일하다는 조건 때문에 

로 주어진다. 이 값과 입력 영상에서 0부터 k까지 그레이 레벨을 갖는 픽셀의 수가 같으면 된다.

입력 영상에서 그레이 값이 0부터 k까지인 픽셀 수는 

총 픽셀 수는 cumulative histogram[255]로 주어지므로 원하는 매핑 j = T [k]는

이 매핑에 의하면 입력 영상의 픽셀이 가지는 가장 큰 그레이 값은 출력 영상에는 255에 해당하게 된다. 출력 영상은 정의에 의해서 cumulative histogram이 직선적으로 증가하는 형태를 가지게 된다. 큰 영상의 경우 이 매핑을 Lookup 테이블로 저장하면 연산의 횟수를 줄일 수 있다.


note)
1. 0차 cumulative histogram을 이용하면 0부터 k까지의 histogram의 합은 한 번의 호출로 얻어진다.
2. 0차 cumulative histogram의 마지막 bin의 값은 전체 픽셀 수의 합이 된다.

// histogram의 합은 cumulative histogram을 구성하면 된다;
void HistogramEqualize(BYTE *src, int width, int height, BYTE* dst) {
    int histogram[256] = {0}, cumhist[256];
    for (int k = width * height; k-->0;) ++histogram[src[k]];
    
    // make 0-st order cumulative histogram;
    cumhist[0] = histogram[0];
    for (int k = 1; k < 256; k++) cumhist[k] = cumhist[k-1] + histogram[k] ;
    
    int map[256];
    double v = 255.0 / cumhist[255]; 	// cumhist[255] = number of pixels;
    for (int k = 0; k < 256; k++) {
    	int v = int(v * cumhist[k]);
        map[k] = v > 255 ? 255: v;
    }
    
    for (int k = width * height; k-->0;) dst[k] = map[src[k]];
};

 

 

사용자 삽입 이미지

 

평탄화 전의 히스토그램(붉은색) 및 누적 히스토그램(연두색)

 

사용자 삽입 이미지

 

 

평탄화 후의 히스토그램(붉은색) 및 누적 히스토그램(연두색)

 

728x90

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

Bright Preserving Histogram Equalization with Maximum Entropy  (0) 2008.07.31
Adaptive Binarization  (2) 2008.07.14
FFT2D  (0) 2008.06.10
Otsu Algorithm  (6) 2008.05.30
Hough Transform  (2) 2008.05.22
,

  • Forward transformation:
    $$X_{k} =  \sum_{n = 0} ^{N-1} x_n e^{ -2i  \pi k n /N }, ~\quad k=0,1,..., N-1;$$
  • Backward transformation:
    $$x_n = \frac{1}{N}\sum_{k = 0} ^{N-1} X_k  e^{ + 2i  \pi k n /N }, ~\quad n=0,1,..., N-1;$$
  • Butterworth Filter:
    $$H(x, y) = \frac{1}{1+\left( \frac{x^2 +y^2}{H_c^2 }\right)^N} ,\quad N=\text{order},~H_c =\text{cut-off};$$
  • 주어진 수가 $2^n$인가?(kipl.tistory.com/84). 입력 데이터 개수가 2의 거듭제곱이 아니면 부족한 부분은 0으로 채워서 $2^n \ge \text{데이터 개수}$ 크기가 되도록 만든다. 

Cooley–Tukey FFT algorithm:  

FFT butterfly diagram

/* 1차원 FFT:: dir=1 for forward, -1 for backward;
** x = real-component, and y = imaginary componet.
** nn = length of data: 2의 거듭제곱이어야 한다.
*/
int fft1d(int dir, int nn, double x[], double y[]) {
    /* Calculate the number of points */
    int m = 0;
    while (nn > 1) {
        nn >>= 1;  m++;
    }
    nn = 1 << m;
    
    /* Do the bit reversal */
    int i2 = nn >> 1;
    int j = 0;
    for (int i = 0; i < nn - 1; i++) {
        if (i < j) { // swap(x[i], x[j]), swap(y[i], y[j]);
            double tx = x[i], ty = y[i];
            x[i] = x[j];  y[i] = y[j];
            x[j] = tx;    y[j] = ty;
        }
        int k = i2;
        while (k <= j) {
            j -= k;
            k >>= 1;
        }
        j += k;
    }
    
    /* Compute the FFT */
    double c1 = -1.0;
    double c2 = 0.0;
    int l2 = 1;
    for (l = 0; l < m; l++) {
        int l1 = l2;
        l2 <<= 1;
        double u1 = 1.0;
        double u2 = 0.0;
        for (int j = 0; j < l1; j++) {
            for (int i = j; i < nn; i += l2) {
                int i1 = i + l1;
                double t1 = u1 * x[i1] - u2 * y[i1];
                double t2 = u1 * y[i1] + u2 * x[i1];
                x[i1] = x[i] - t1;
                y[i1] = y[i] - t2;
                x[i] += t1;
                y[i] += t2;
            }
            double z = u1 * c1 - u2 * c2;
            u2 = u1 * c2 + u2 * c1;
            u1 = z;
        }
        c2 = sqrt((1.0 - c1) / 2.0);
        if (dir == 1) c2 = -c2;
        c1 = sqrt((1.0 + c1) / 2.0);
    }
    
    /* Scaling for forward transform */
    if (dir == 1) {
        for (i = 0; i < nn; i++) {
            x[i] /= double(nn);
            y[i] /= double(nn);
        }
    }
    return 1;
};
/* 2차원 FFT. data = (2 * nn) x mm 크기의 버퍼;
** 각 행은 nn개의 실수 성분 다음에 nn개의 허수 성분이 온다.
** nn, mm은 각각 행과 열은 나타내며, 2의 거듭제곱이어야 한다.
** isign = 1 for forward, -1 for backward. 
** copy = buffer of length 2*mm ;
*/
void fft2d(double* data, int nn, int mm, int isign, double* copy) { 
    int stride = nn << 1; //row_stride;
    /* Transform by ROWS for forward transform */
    if (isign == 1) {
        int index1 = 0;
        for (int i = 0; i < mm; i++) {
            // real = &data[index1], imag = &data[index1 + nn];
            fft1d(isign, nn, &data[index1], &data[index1 + nn]);
            index1 += stride;
        }
    }
    
    /* Transform by COLUMNS */
    for (int j = 0; j < nn; j++) {
        /* Copy pixels into temp array */
        int index1 = j ;
        int index2 = 0;
        for (i = 0; i < mm; i++) {
            copy[index2] = data[index1];
            copy[index2 + mm] = data[index1 + nn];
            index2++;
            index1 += stride;
        }
        
        /* Perform transform */
        fft1d(isign, mm, &copy[0], &copy[mm]);
        
        /* Copy pixels back into data array */
        index1 = j;
        index2 = 0;
        for (i = 0; i < mm; i++) {
            data[index1] = copy[index2];
            data[index1 + mm] = copy[index2 + mm];
            index2++;
            index1 += stride;
        }
    }
    
    /* Transform by ROWS for inverse transform */
    if (isign == -1) {
        int index1 = 0;
        for (i = 0; i < mm; i++) {
            fft1d(isign, nn, &data[index1], &data[index1 + nn]);
            index1 += stride;
        }
    }
}

void butterworth( double * data, int Xdim, int Ydim, 
                 int Homomorph, int LowPass,
                 double Power, double CutOff, double Boost);

더보기
// H * F;
void butterworth(double * data, int Xdim, int Ydim, 
                 int Homomorph, int LowPass,
                 double Power, double CutOff, double Boost) 
{
    double CutOff2 = CutOff * CutOff;
    /* Prepare to filter */
    int stride = Xdim << 1;  // width of a sigle row(real + imag);    
    int halfx = Xdim >> 1;
    int halfy = Ydim >> 1;
    double *rdata = &data[0];   //real part;
    double *idata = &data[Xdim];//imag part;
    /* Loop over Y axis */
    for (int y = 0; y < Ydim; y++) {
        int y1 = (y < halfy) ? y: y - Ydim;
        /* Loop over X axis */
        for (int x = 0; x < Xdim; x++) {
            int x1 = (x < halfx) ? x: x - Xdim;
            /* Calculate value of Butterworth filter */
            double filter;
            if (LowPass)
                Filter = (1 / (1 + pow((x1 * x1 + y1 * y1) / CutOff2, Power)));
            else if ((x1 != 0) || (y1 != 0))
                Filter = (1 / (1 + pow( CutOff2 / (x1 * x1 + y1 * y1), Power)));
            else
                Filter = 0.0;
            if (Homomorph)
                Filter = Boost + (1 - Boost) * Filter;
            /* Do pointwise multiplication */
            rdata[x] *= Filter;
            idata[x] *= Filter;
        };
        rdata += stride; //go to next-line;
        idata += stride; 
    }
};


Butterworth Low Pass filter 적용의 예(order=2, cutoff=20)

사용자 삽입 이미지

 

PowerSpectrum 변화

사용자 삽입 이미지

 

728x90

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

Bright Preserving Histogram Equalization with Maximum Entropy  (0) 2008.07.31
Adaptive Binarization  (2) 2008.07.14
Histogram Equalization  (0) 2008.06.22
Otsu Algorithm  (6) 2008.05.30
Hough Transform  (2) 2008.05.22
,