Ref: https://grail.cs.washington.edu/projects/digital-matting/image-matting/  

세번째 이미지 = alpha 값이 적용된 전경(Gandalf)

// knockout method;
// trimap을 만들 때 전경/배경/중간 영역(경계) 구분;
#define BACKGROUND 0    // 배경 마스크 색상;
#define FOREGROUND 255  // 전경 마스크 색상;
#define UNKNOWN    128  // 경계 영역의 생상;
#define EDGEMARK   3    // 전경과 배경의 경계선을 표시하는 색상
// 주어진 픽셀에서 경계점까지 떨어진 거리(제곱)에 따라 경계점을 
// 정렬하기 위해 필요함;
struct PtDist {
    int x, y, d;
    PtDist() {}
    PtDist(int x, int y): x(x), y(y), d(0) {}
};
struct RGBTRIO {
    double rgb[3];  // b-g-r
};
static 
bool compare(const PtDist& a, const PtDist& b) {
    // 거리 제곱의 ascending order로 정렬;
    return a.d < b.d;
}
// 최소거리의 DISTANCE_THRESHOLD배 이내에 있는 경계 픽셀만을 이용해서 
// alpha값을 추정한다:
#define DISTANCE_THRESHOLD  (5.0)
static
RGBTRIO estimateColor(int x0, int y0, CRaster& raster, 
                   std::vector<PtDist>& pts) {
    for (int i = pts.size(); i-->0;) {
        int x = pts[i].x - x0; 
        int y = pts[i].y - y0;
        pts[i].d = x * x + y * y; 
    }
    std::sort(pts.begin(), pts.end(), compare);
    const double minDist = sqrt(double(pts[0].d));
    ASSERT(minDist > 0);
    const double maxDist = DISTANCE_THRESHOLD * minDist;
    const double inv_diff = 1.0/ (maxDist - minDist);
    double wsum = 0, rsum = 0, gsum = 0, bsum = 0, d;
    for (int i = 0; i < pts.size(); i++) { // sorted data;
        if ((d = sqrt(double(pts[i].d))) < maxDist) {
            double w = (maxDist - d) * inv_diff;
            DWORD q = raster.GetPixel0(pts[i].x, pts[i].y);
            bsum += w * ((q      ) & 0xFF); // blue;
            gsum += w * ((q >>  8) & 0xFF); // green;
            rsum += w * ((q >> 16) & 0xFF); // red;
            wsum += w;
        }
        else break;
    }
    if (wsum == 0) wsum = 1;
    RGBTRIO Q;
    Q.rgb[0] = (bsum / wsum);
    Q.rgb[1] = (gsum / wsum);
    Q.rgb[2] = (rsum / wsum);
    return Q;
}
#define AddVec(A,B,C)	{C[0]=A[0]+B[0];C[1]=A[1]+B[1];C[2]=A[2]+B[2];}
#define SubVec(A,B,C)	{C[0]=A[0]-B[0];C[1]=A[1]-B[1];C[2]=A[2]-B[2];}
#define NormSq(A)       (A[0]*A[0]+A[1]*A[1]+A[2]*A[2])
#define DotProd(A,B)	(A[0]*B[0]+A[1]*B[1]+A[2]*B[2])
#define ScaleVec(A,s)   {A[0]*=s;A[1]*=s;A[2]*=s;}
static
double estimateAlpha(double F[3], double B[3], double C[3]) {
    double N[3], V[3], Bp[3];
    SubVec(F, B, N);						//N=F-B;
    double nn = NormSq(N);					//nn=N.N;
    if (nn == 0) return 1;	
    SubVec(C, B, V);						//V=C-B;
    double proj = DotProd (N, V) / nn;		//proj=(V.N)/(N.N);
    ScaleVec(N, proj);						//N=N(V.N)/(N.N);
    SubVec(C, N, Bp);						//Bp=C-N(V.N)/(N.N);
    //
    SubVec(F, Bp, N);						//N=F-Bp;
    nn = NormSq(N);							//nn=N.N;
    if (nn == 0) return 1;
    SubVec(C, Bp, V);						//V=C-Bp;
    proj = DotProd(N, V) / nn;				//proj=(N.V)/(N.N);

    return proj < 0 ? 0: proj > 1 ? 1: proj;
}
void FindMatt(CRaster& raster, CRaster& trimap, CRaster& alphaMap) {
    ASSERT((trimap.GetBPP()==8) && (raster.GetBPP()==24));
    const CSize sz = raster.GetSize();
    alphaMap.SetDimensions(sz, 32);// Bitmap with alpha-channel;
    std::vector<PtDist> ptFore = binaryTracing(trimap, FOREGROUND);
    std::vector<PtDist> ptBack = binaryTracing(trimap, BACKGROUND);
    
    double C[3];
    for (int y = 0; y < sz.cy; y++) {
        BYTE *p = (BYTE *)trimap.GetLinePtr(y);
        BYTE *q = (BYTE *)raster.GetLinePtr(y);
        BYTE *r = (BYTE *)alphaMap.GetLinePtr(y);
        for (int x = 0; x < sz.cx; x++) {
            if (UNKNOWN == *p) {// unknown pixel(0=background,255=foreground);
                RGBTRIO Qf = estimateColor(x, y, raster, ptFore);
                RGBTRIO Qb = estimateColor(x, y, raster, ptBack);
                C[0] = q[0]; C[1] = q[1]; C[2] = q[2];
                double alpha = estimateAlpha(Qf.rgb, Qb.rgb, C);
                // if ( alpha >= 0.5)
                r[0] = (int)max(0, min(255, Qf.rgb[0]));
                r[1] = (int)max(0, min(255, Qf.rgb[1]));
                r[2] = (int)max(0, min(255, Qf.rgb[2]));
                r[3] = int(alpha * 255);       //[0:1]->[0:255]
            } else if (FOREGROUND == *p) {
                r[0] = q[0];
                r[1] = q[1];
                r[2] = q[2];
                r[3] = 0xFF;
            }
            p++; q += 3; r += 4;
        }
    }
}
728x90

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

Anisotropic Diffusion Filter (2)  (0) 2024.02.23
Edge Preserving Smoothing  (0) 2024.02.14
Watershed Segmentation  (0) 2021.02.27
Local Ridge Orientation  (0) 2021.02.21
Contrast Limited Adaptive Histogram Equalization (CLAHE)  (3) 2021.02.15
,