이미지를 형성할 때 조명의 조건이 균일하지 않으면 이미지 처리과정에서 복잡성이 증가하게 된다. 따라서 이미지 의 전처리 과정으로 배경의 불균일성을 제거하여야 할 필요가 생기는 경우가 많다. 일반적으로 배경의 불균일성은 공간적으로 넓은 범위에서 천천이 일어나기 때문에, 이러한 사실을 이용하면 이미지상에서 배경에 의한 효과를 제거할 수 있다. 어떤 경우에는 큰(전경의 물체의 크기에 비해서) 윈도우를 가지는 메디안 필터를 연속적으로 적용하여서 배경을 얻을 수 있다. 그러나 이 과정은 계산비용이 상당하다. 다른 방법으로는 이미지의 픽셀값을 z-값으로 하면 (x,y,z=픽셀값)을 지나는 평면의 방정식을 최소자승법으로 구하여서 배경으로 이용할 수 있다. 이 경우에는 전경의 물체가 최소자승피팅에 있어서 outlier역할을 하여서 좀더 robust한 피팅방법을 이용하여야 한다.
또 다른 방법으로는 이미지가 3차원 공간에서 surface(x,y,z=픽셀값)을 형성하므로, 만약에 전경(밝은색)이 전체 이미지중에서 차지하는 비중이 작은 경우에는 아래의 그림처럼(A)충분히 큰 공을 표면에 접하도록 굴리면, 구르는 공의 표면이 그리는  surface를 배경으로 생각할 수 있다(B는 전경이 어두운 경우).

사용자 삽입 이미지

사용자 삽입 이미지


만약에 충분히 반경이 큰 공을 사용하게 되면 부드러운 배경의 표면을 얻을 수 있다. 공표면과 이미지 표면과의 접촉관계는 공의 표면에 놓인 patch의 좌표점에서(미리계산) 높이를 이미지의 픽셀값과 비교하여서 얻느다. 공의 반경이 큰 경우에는 연산을 줄이기 위해서 원본이미지를 축소하여서 작업을 하고, 간단한 보간에 의해서 원본크기로 만들면 된다.


아래의 밝은 배경에 어두운 전경인 경우에 대해서 이것을 적용한 예인데 먼저, negative 이미지를  만들어서(어두운 배경에 밝은 전경) rolling ball transformation을 적용하여 배경을 얻은 것이다.


사용자 삽입 이미지


 negative image:

사용자 삽입 이미지

radius=16;

사용자 삽입 이미지

radius=5:

사용자 삽입 이미지

배경을 뺀 결과(radius=16):

사용자 삽입 이미지


// processing을 빠르게 하기 위해서, ball의 반경이 큰 경우에는 원본이미지를
// 축소하여서(2,4,8,..) 처리한 후에, 처리 안된 점들은 interpolation을 하여서
// bacground image을 생성한다.
void RollBall(RollingBall& ball,                        //rolling ball object;
              BYTE *smallImage, int swidth, int sheight,//shrinked source image;
              BYTE *background) //shrinked background image
{
    int wsz   =  ball.patchwidth ;   //patch width(odd);
    int hwsz = wsz>>1;        
    BYTE *patch = &ball.data[0] ;           //pre-calculated patch z-value;
    int zctr = 0; // start z-center in the xy-plane
    for (int y=0; y<sheight; y++) {
            for (int x=0; x<swidth; x++) {
            // 현재의 패치내에서(중심(x-patchwidth/2, y-patchwidth/2)) 픽셀의 z값(그레이레벨)과
            // zctr만큼 shift한 공의 윗표면에 놓인 패치의 z-값간의 차이를 구함. 그 값의 차이가
            // 가장 작을 때의 값만큼 공의 중심을 높이거나, 내리면 공과 이미지의 픽셀 surface가 밑에서
            // 접하게 된다.
            int zmin = 255;
            int yend = y+hwsz;
            int xend = x+hwsz;
            for(int yy=y-hwsz, ballpt=0; yy<=yend; yy++){
                for(int xx=x-hwsz, imgpt=yy*swidth+xx; xx<=xend; xx++){
                    if(xx>=0 && xx<swidth && yy>=0 && yy<sheight) {
                        int zdif = smallImage[imgpt] - (zctr + (patch[ballpt]));
                        if (zdif<zmin) zmin = zdif;
                    }
                    ballpt++; imgpt++;
                }
            }
            if (zmin!=0) zctr += zmin;
            // zmin<0 인 경우는 x의 오른쪽 반만 처리하면 된다(not yet);
            for(yy=y-hwsz, ballpt=0; yy<=yend; yy++){
                for(int xx=x-hwsz, imgpt=yy*swidth+xx; xx<=xend; xx++){
                    if(xx>=0 && xx<swidth && yy>=0 && yy<sheight) {
                        int zadd = zctr + (patch[ballpt]); 
                        if (zadd>(sbackground[imgpt]))          
                            sbackground[imgpt] = (BYTE)zadd;
                    }
                    ballpt++; imgpt++;
                }
            }
        }
    }
}

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

Spline Based Snake  (0) 2008.08.15
Anisotropic Diffusion Filter  (0) 2008.08.11
Rolling Ball Transformation  (1) 2008.08.09
Mean Shift Filter  (5) 2008.08.06
Chamfer Match  (0) 2008.08.01
Retinex Algorithm  (2) 2008.07.26
Posted by helloktk

댓글을 달아 주세요

  1. helloktk 2012.01.17 19:26 신고  댓글주소  수정/삭제  댓글쓰기

    #define SQR(x) ((x)*(x))
    struct RollingBall {
    std::vector<BYTE> data;
    int patchwidth;
    int shrinkfactor;
    RollingBall(int radius) {
    int artrimper;
    if (radius<=10) {
    shrinkfactor = 1;
    artrimper = 12; // trim 24% in x and y
    } else if (radius<=30) {
    shrinkfactor = 2;
    artrimper = 12; // trim 24% in x and y
    } else if (radius<=100) {
    shrinkfactor = 4;
    artrimper = 16; // trim 32% in x and y
    } else {
    shrinkfactor = 8;
    artrimper = 20; // trim 40% in x and y
    }
    Build(radius, artrimper);
    }
    void Build(int ballradius, int artrimper) {
    int sballrad = ballradius/shrinkfactor;
    if (sballrad<1) sballrad = 1;
    int rsquare = SQR(sballrad);
    int diam = sballrad*2;
    int xtrim = (artrimper*diam)/100; // only use a patch of the rolling ball
    patchwidth = diam - xtrim - xtrim;
    int halfpatchwidth = sballrad - xtrim;
    int ballsize = SQR(patchwidth+1);
    data.resize(ballsize);

    for (int i=0; i<ballsize; i++) {
    int xval = i % (patchwidth+1) - halfpatchwidth; //relative to patch center-x.
    int yval = i / (patchwidth+1) - halfpatchwidth; //relative to patch center-y;
    int temp = rsquare - SQR(xval) - SQR(yval);
    if (temp >= 0)
    data[i] = int(sqrt(temp));
    else
    data[i] = 0;
    }
    }
    };