diff --git a/src/nvimage/HoleFilling.cpp b/src/nvimage/HoleFilling.cpp index 928e9ea..363f97b 100644 --- a/src/nvimage/HoleFilling.cpp +++ b/src/nvimage/HoleFilling.cpp @@ -11,6 +11,27 @@ using namespace nv; +float BitMap::sampleLinearClamp(float x, float y) const +{ + const float fracX = x - floor(x); + const float fracY = y - floor(y); + + const uint ix0 = ::clamp(uint(floor(x)), 0U, m_width-1); + const uint iy0 = ::clamp(uint(floor(y)), 0U, m_height-1); + const uint ix1 = ::clamp(uint(floor(x))+1, 0U, m_width-1); + const uint iy1 = ::clamp(uint(floor(y))+1, 0U, m_height-1); + + float f1 = bitAt(ix0, iy0); + float f2 = bitAt(ix1, iy0); + float f3 = bitAt(ix0, iy1); + float f4 = bitAt(ix1, iy1); + + float i1 = lerp(f1, f2, fracX); + float i2 = lerp(f3, f4, fracX); + + return lerp(i1, i2, fracY); +} + // This is a variation of Sapiro's inpainting method. void nv::fillExtrapolate(int passCount, FloatImage * img, BitMap * bmap) @@ -26,6 +47,7 @@ void nv::fillExtrapolate(int passCount, FloatImage * img, BitMap * bmap) nvCheck(bmap->height() == uint(h)); AutoPtr newbmap(new BitMap(w, h)); + newbmap->clearAll(); for(int p = 0; p < passCount; p++) { @@ -210,11 +232,13 @@ void nv::fillVoronoi(FloatImage * img, const BitMap * bmap) for( x = 0; x < w; x++ ) { const int sx = edm[y * w + x].x; const int sy = edm[y * w + x].y; - nvDebugCheck(sx < w && sy < h); - - if( sx != x || sy != y ) { - for(int c = 0; c < count; c++ ) { - img->setPixel(img->pixel(sx, sy, c), x, y, c); + + if (sx < w && sy < h) + { + if (sx != x || sy != y) { + for(int c = 0; c < count; c++ ) { + img->setPixel(img->pixel(sx, sy, c), x, y, c); + } } } } @@ -247,7 +271,9 @@ static bool downsample(const FloatImage * src, const BitMap * srcMask, const Flo FloatImage * dst = new FloatImage(); dst->allocate(count, nw, nh); + BitMap * dstMask = new BitMap(nw, nh); + dstMask->clearAll(); for(uint c = 0; c < count; c++) { for(uint y = 0; y < nh; y++) { @@ -358,6 +384,80 @@ void nv::fillPullPush(FloatImage * img, const BitMap * bmap) deleteAll(mipmapMasks); } +// It looks much cooler with trilinear filtering +void nv::fillPullPushLinear(FloatImage * img, const BitMap * bmap) +{ + nvCheck(img != NULL); + + const uint count = img->componentNum(); + const uint w = img->width(); + const uint h = img->height(); + const uint num = log2(max(w,h)); + + // Build mipmap chain. + Array mipmaps(num); + Array mipmapMasks(num); + + mipmaps.append(img); + mipmapMasks.append(bmap); + + const FloatImage * current; + const BitMap * currentMask; + + // Compute mipmap chain. + while(downsample(mipmaps.back(), mipmapMasks.back(), ¤t, ¤tMask)) + { + mipmaps.append(current); + mipmapMasks.append(currentMask); + } + + // Sample mipmaps until non-hole is found. + for(uint y = 0; y < h; y++) { + for(uint x = 0; x < w; x++) { + + float sx = x; + float sy = y; + + float coverageSum = 0.0f; + + const uint levelCount = mipmaps.count(); + for (uint l = 0; l < levelCount; l++) + { + const float fx = sx / mipmaps[l]->width(); + const float fy = sy / mipmaps[l]->height(); + + float coverage = mipmapMasks[l]->sampleLinearClamp(sx, sy); + + if (coverage > 0.0f) + { + // Sample mipmaps[l](sx, sy) and copy to img(x, y) + for(uint c = 0; c < count; c++) { + img->addPixel((1 - coverageSum) * mipmaps[l]->sampleLinearClamp(fx, fy, c), x, y, c); + } + + coverageSum += coverage; + + if (coverageSum >= 1.0f) + { + break; + } + } + + sx /= 2; + sy /= 2; + } + } + } + + // Don't delete the original image and mask. + mipmaps[0] = NULL; + mipmapMasks[0] = NULL; + + // Delete the mipmaps. + deleteAll(mipmaps); + deleteAll(mipmapMasks); +} + /* @@ -654,6 +754,7 @@ void nv::fillQuadraticExtrapolate(int passCount, FloatImage * img, BitMap * bmap nvCheck(bmap->height() == uint(h)); AutoPtr newbmap( new BitMap(w, h) ); + newbmap->clearAll(); float * coverageChannel = NULL; if (coverageIndex != -1) diff --git a/src/nvimage/HoleFilling.h b/src/nvimage/HoleFilling.h index 53fbf71..cb97911 100644 --- a/src/nvimage/HoleFilling.h +++ b/src/nvimage/HoleFilling.h @@ -22,6 +22,8 @@ namespace nv uint width() const { return m_width; } uint height() const { return m_height; } + float sampleLinearClamp(float x, float y) const; + bool bitAt(uint x, uint y) const { nvDebugCheck(x < m_width && y < m_height); @@ -86,6 +88,7 @@ namespace nv NVIMAGE_API void fillVoronoi(FloatImage * img, const BitMap * bmap); NVIMAGE_API void fillPullPush(FloatImage * img, const BitMap * bmap); + NVIMAGE_API void fillPullPushLinear(FloatImage * img, const BitMap * bmap); NVIMAGE_API void fillExtrapolate(int passCount, FloatImage * img, BitMap * bmap); NVIMAGE_API void fillQuadraticExtrapolate(int passCount, FloatImage * img, BitMap * bmap, int coverageIndex = -1);