Ref: https://grail.cs.washington.edu/projects/digital-matting/image-matting/
// 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 |