티스토리 툴바

Retinex 알고리즘은 영상의 밝기와 시각적으로 인지된 감각 사이에는 로그의 관계를 가진다는 사실과, 영상의 밝기는 실제의 밝기인 반사성분과 조명에 의한 성분의 곱으로 주어진다는 실험적인 사실에 근거하여서 영상에서 조명의 성분을 줄이고, 반사의 성분만을 나타냄으로써 영상의 콘트라스트를 증대시키고자 하는 시도이다.
   
     B(x,y) = R(x,y) x I(x,y);
where
     B(x,y) = observed image;
     R(x,y) = perceivd reflectance(반사성분);
     I(x, y) = perceive illumination(조명성분);
==>
     log{R(x,y)} = log{B(x,y)} - log{I(x,y)};

조명성분을 구하기 위해서는 영상의 디테일에 의존하지 않는 큰 스케일에서의 영상성분만 보면 된다(low frequency). 따라서 조명성분은 원영상을 적당한 스케일로 블러링시킨 영상에서 얻을 수 있다(I~Gauss*B). 물론, 여러 개의 스케일을 조합을 하여서 사용을 할 수 있다. 
멀티스케일을 적용하는 경우에 Retinex 알고리즘에서 반사성분은, 따라서,
  
   dst[i]= sum_{s}{log(src[i])  - log((gauss_filter(s) * src)[i])}; s=scale with equal weight.

이다. 그리고 RGB컬러 영상에서는 각각의 성분에 대해서 적용을 하면 된다. 아래의 코드는 그레이 영상에 대해서 적용이 가능한 코드이다. RGB의 경우도 쉽게 확장이 된다.

Q: How to find a optimal scale distribution for a given image?

#define SQR(x) ((x)*(x))
// recursive 가우시안 필터의 계수계산;
void compute_coefs3 (double c[5], double sigma) {
    /*
    *  "Recursive Implementation of the gaussian filter.",
    *   Ian T. Young , Lucas J. Van Vliet, Signal Processing 44, Elsevier 1995.
    */
    double q = 0;  
    if (sigma >= 2.5)
        q = 0.98711 * sigma - 0.96330;
    else if ((sigma >= 0.5) && (sigma < 2.5))
        q = 3.97156 - 4.14554 * (double) sqrt ((double) 1 - 0.26891 * sigma);
    else
        q = 0.1147705018520355224609375;
   
    double q2 = q * q;
    double q3 = q * q2;
    c[0] = (1.57825+(2.44413*q)+(1.4281 *q2)+(0.422205*q3));
    c[1] = (        (2.44413*q)+(2.85619*q2)+(1.26661 *q3));
    c[2] = (                   -((1.4281*q2)+(1.26661 *q3)));
    c[3] = (                                 (0.422205*q3));
    c[4] = 1.0 - (c[1]+c[2]+c[3])/c[0]; //=B*;
}
// 멀티스케일 설정;
void retinex_scale_distribution(const int nscales/*=3*/, const int s/*=240*/,
                                double scales[])
{
    //  ASSERT(nscales>=3);
    double size_step = (double) s / (double) nscales;
    for(int i = 0; i < nscales; ++i)
        scales[i] = 2. + (double) i * size_step;
}
// 가우시안  convolution(horizontal)
void gauss_smooth (double *src, int width, int height, double *out, double b[5]) {
    int bufsize = width+3;
    std::vector<double> w1(bufsize);
    std::vector<double> w2(bufsize);
    double invb0 = 1./b[0];
    for(int y = 0; y < height; y++) {
        /* forward pass */
        double *src_ptr = &src[y*width];
        w1[0]=src_ptr[0];
        w1[1]=src_ptr[0];
        w1[2]=src_ptr[0];
        for (int x = 0; x < width ; x++) {
            w1[x+3] = b[4]*src_ptr[x] + ((b[1]*w1[x+2] + b[2]*w1[x+1] + b[3]*w1[x])*invb0);
        }        
        /* backward pass : out ==>transposed;*/
        w2[width+0]= w1[width+2];
        w2[width+1]= w1[width+2];
        w2[width+2]= w1[width+2];
        for (x=width-1; x >= 0; x--){
            w2[x]= out[x*height+y] = b[4]*w1[x] + ((b[1]*w2[x+1] +  b[2]*w2[x+2] + b[3]*w2[x+3] )*invb0);
        }
    }
};
// 이미지의 평균과 표준편차 계산;
void image_statistics(double *img, int size/*=width*height*/,
                     double *mean, double *std) {
    double s=0, ss=0;
    for(int i=0; i<size; i++){
        double a = img[i];
        s += a;
        ss += SQR(a) ;
    } ;
    *mean = s / size ;
    *std = sqrt((ss - s*s/size)/size);
}
//
void rescale_range(double *data, int size) {
    double mean, sig;
    image_statistics(&data[0], size, &mean, &sig);
 
    double max_val = mean + 1.2 * sig;
    double min_val = mean - 1.2 * sig;   
    double range = max_val - min_val;
    if(!range) range=1.0;
    range = 255./range ;
    // change the range;
    for(int i=0; i<size; i++){
        data[i] = (data[i]-min_val)*range;
    }
}
// 메인.
void retinex_process(double *src, int width, int height, double *dst) {
    const int nfilter = 3;
    double sigma[nfilter];
    int default_scale=240;
    retinex_scale_distribution(nfilter, default_scale, sigma);
    TRACE("sigma::%f, %f, %f\n", sigma[0], sigma[1], sigma[2]);

    std::vector<double> scaleimg[nfilter];
    //allocate filtered image;
    for(int i=0; i<nfilter; i++)
        scaleimg[i].resize(width * height);

    //scale-space gauss_smooth;
    for(i=0; i<nfilter; i++) {
        double c[5];
        compute_coefs3(c, sigma[i]);
        // seperable gaussian convolution;
        //(1)horizontal convolution; (wxh--> hxw); and dst as a temp-buffer;
        gauss_smooth(&src[0], width, height, &dst[0], c);
        //(2)vertical convoution;(hxw--> wxh);
        gauss_smooth(&dst[0], height, width, &((scaleimg[i])[0]), c);
 }    }
    //reset dst-image;
    for(i=0; i<width*height; i++)
        dst[i] = 0;
   
    //accumulate dst-image;
    for(i=0; i<nfilter; i++) {
        std::vector<double>& filtered=scaleimg[i];
        for(int k=0; k<width*height; k++)
            dst[k]+= log(src[k]+1.) - log(filtered[k]+1.);
    }
   
    //scale to [0,255];
    rescale_range(&dst[0], width, height); 
}

사용자 삽입 이미지
scale=240
사용자 삽입 이미지

크리에이티브 커먼즈 라이선스
Creative Commons License

'이미지인식' 카테고리의 다른 글

Mean Shift Filter  (3) 2008/08/06
Chamfer Match  (0) 2008/08/01
Retinex Algorithm  (0) 2008/07/26
RANSAC : Circle Fit  (0) 2008/07/21
KMeans Algorithm  (0) 2008/07/19
Robust Line Fitting  (0) 2008/07/08
by helloktk 2008/07/26 21:52
| 1 ... 76 77 78 79 80 81 82 83 84 ... 112 |