|
|
|
@ -479,11 +479,15 @@ public: |
|
|
|
void LocalSeamLevelingExternalUV(); |
|
|
|
void LocalSeamLevelingExternalUV(); |
|
|
|
|
|
|
|
|
|
|
|
void GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& baseFileName, bool bOriginFaceview, Scene *pScene); |
|
|
|
void GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& baseFileName, bool bOriginFaceview, Scene *pScene); |
|
|
|
|
|
|
|
void GenerateTextureForUV(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& baseFileName, bool bOriginFaceview, Scene *pScene); |
|
|
|
void GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& baseFileName); |
|
|
|
void GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& baseFileName); |
|
|
|
bool TextureWithExistingUV(const IIndexArr& views, int nIgnoreMaskLabel, float fOutlierThreshold, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight); |
|
|
|
bool TextureWithExistingUV(const IIndexArr& views, int nIgnoreMaskLabel, float fOutlierThreshold, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight); |
|
|
|
Mesh::Image8U3Arr GenerateTextureAtlasFromUV(const LabelArr& faceLabels, const IIndexArr& views, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight); |
|
|
|
Mesh::Image8U3Arr GenerateTextureAtlasFromUV(const LabelArr& faceLabels, const IIndexArr& views, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight); |
|
|
|
Point2f ProjectPointWithAutoCorrection(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage); |
|
|
|
Point2f ProjectPointWithAutoCorrection(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage); |
|
|
|
Point2f ProjectPointRobust(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage, float searchRadius = 0.02f); |
|
|
|
Point2f ProjectPointRobust(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage, float searchRadius = 0.02f); |
|
|
|
|
|
|
|
Mesh::Image8U3Arr SampleFromExistingTextures(const LabelArr& faceLabels, const IIndexArr& views, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight); |
|
|
|
|
|
|
|
Pixel8U SampleTextureBilinear(const Image8U3& texture, const Point2f& texCoord); |
|
|
|
|
|
|
|
void ApplySharpening(Image8U3& texture, float fSharpnessWeight); |
|
|
|
bool ValidateProjection(const Vertex& worldPoint, const Image& sourceImage, Point2f imgPoint, float maxReprojectionError = 1.5f); |
|
|
|
bool ValidateProjection(const Vertex& worldPoint, const Image& sourceImage, Point2f imgPoint, float maxReprojectionError = 1.5f); |
|
|
|
Pixel8U SampleImageBilinear(const Image8U3& image, const Point2f& point); |
|
|
|
Pixel8U SampleImageBilinear(const Image8U3& image, const Point2f& point); |
|
|
|
void ProjectFaceToTexture(FIndex faceID, IIndex viewID, const TexCoord* uv, Image8U3& texture); |
|
|
|
void ProjectFaceToTexture(FIndex faceID, IIndex viewID, const TexCoord* uv, Image8U3& texture); |
|
|
|
@ -8830,6 +8834,368 @@ void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLevel |
|
|
|
out.close(); |
|
|
|
out.close(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void MeshTexture::GenerateTextureForUV(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& basename, bool bOriginFaceview, Scene *pScene) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
int border = 2; |
|
|
|
|
|
|
|
if (!bOriginFaceview) |
|
|
|
|
|
|
|
border = 4; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
faceTexcoords.resize(faces.size()*3); |
|
|
|
|
|
|
|
faceTexindices.resize(faces.size()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef TEXOPT_USE_OPENMP |
|
|
|
|
|
|
|
// LOG_OUT() << "def TEXOPT_USE_OPENMP" << std::endl;
|
|
|
|
|
|
|
|
const unsigned numPatches(texturePatches.size()-1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 修改:只在非外部UV模式下执行投影计算 =====
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
#pragma omp parallel for schedule(dynamic) |
|
|
|
|
|
|
|
for (int_t idx=0; idx<(int_t)numPatches; ++idx) { |
|
|
|
|
|
|
|
TexturePatch& texturePatch = texturePatches[(uint32_t)idx]; |
|
|
|
|
|
|
|
#else |
|
|
|
|
|
|
|
for (TexturePatch *pTexturePatch=texturePatches.Begin(), *pTexturePatchEnd=texturePatches.End()-1; pTexturePatch<pTexturePatchEnd; ++pTexturePatch) { |
|
|
|
|
|
|
|
TexturePatch& texturePatch = *pTexturePatch; |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
const Image& imageData = images[texturePatch.label]; |
|
|
|
|
|
|
|
AABB2f aabb(true); |
|
|
|
|
|
|
|
for (const FIndex idxFace: texturePatch.faces) { |
|
|
|
|
|
|
|
const Face& face = faces[idxFace]; |
|
|
|
|
|
|
|
TexCoord* texcoords = faceTexcoords.data()+idxFace*3; |
|
|
|
|
|
|
|
for (int i=0; i<3; ++i) { |
|
|
|
|
|
|
|
texcoords[i] = imageData.camera.ProjectPointP(vertices[face[i]]); |
|
|
|
|
|
|
|
ASSERT(imageData.image.isInsideWithBorder(texcoords[i], border)); |
|
|
|
|
|
|
|
aabb.InsertFull(texcoords[i]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (false) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
const Point2f& a = texcoords[0]; |
|
|
|
|
|
|
|
const Point2f& b = texcoords[1]; |
|
|
|
|
|
|
|
const Point2f& c = texcoords[2]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算边向量
|
|
|
|
|
|
|
|
Point2f v0 = b - a; |
|
|
|
|
|
|
|
Point2f v1 = c - a; |
|
|
|
|
|
|
|
float denom = (v0.x * v0.x + v0.y * v0.y) * (v1.x * v1.x + v1.y * v1.y) - |
|
|
|
|
|
|
|
std::pow(v0.x * v1.x + v0.y * v1.y, 2); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 处理退化三角形情况(面积接近0)
|
|
|
|
|
|
|
|
const float epsilon = 1e-6f; |
|
|
|
|
|
|
|
if (std::abs(denom) < epsilon) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// DEBUG_EXTRA("PointInTriangle - Degenerate triangle, denom=%.10f", denom);
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
DEBUG_EXTRA("PointInTriangle Yes idxFace=%d", idxFace); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// compute relative texture coordinates
|
|
|
|
|
|
|
|
ASSERT(imageData.image.isInside(Point2f(aabb.ptMin))); |
|
|
|
|
|
|
|
ASSERT(imageData.image.isInside(Point2f(aabb.ptMax))); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (bOriginFaceview) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
texturePatch.rect.x = FLOOR2INT(aabb.ptMin[0])-border; |
|
|
|
|
|
|
|
texturePatch.rect.y = FLOOR2INT(aabb.ptMin[1])-border; |
|
|
|
|
|
|
|
texturePatch.rect.width = CEIL2INT(aabb.ptMax[0]-aabb.ptMin[0])+border*2; |
|
|
|
|
|
|
|
texturePatch.rect.height = CEIL2INT(aabb.ptMax[1]-aabb.ptMin[1])+border*2; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
texturePatch.rect.x = std::max(0, FLOOR2INT(aabb.ptMin[0])-border); |
|
|
|
|
|
|
|
texturePatch.rect.y = std::max(0, FLOOR2INT(aabb.ptMin[1])-border); |
|
|
|
|
|
|
|
// 限制尺寸不超过图像实际范围
|
|
|
|
|
|
|
|
const cv::Mat& img = images[texturePatch.label].image; |
|
|
|
|
|
|
|
texturePatch.rect.width = std::min( |
|
|
|
|
|
|
|
CEIL2INT(aabb.ptMax[0]-aabb.ptMin[0])+border*2, |
|
|
|
|
|
|
|
img.cols - texturePatch.rect.x |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
texturePatch.rect.height = std::min( |
|
|
|
|
|
|
|
CEIL2INT(aabb.ptMax[1]-aabb.ptMin[1])+border*2, |
|
|
|
|
|
|
|
img.rows - texturePatch.rect.y |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ASSERT(imageData.image.isInside(texturePatch.rect.tl())); |
|
|
|
|
|
|
|
ASSERT(imageData.image.isInside(texturePatch.rect.br())); |
|
|
|
|
|
|
|
const TexCoord offset(texturePatch.rect.tl()); |
|
|
|
|
|
|
|
for (const FIndex idxFace: texturePatch.faces) { |
|
|
|
|
|
|
|
TexCoord* texcoords = faceTexcoords.data()+idxFace*3; |
|
|
|
|
|
|
|
for (int v=0; v<3; ++v) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
texcoords[v] -= offset; |
|
|
|
|
|
|
|
if (false) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
const Point2f& a = texcoords[0]; |
|
|
|
|
|
|
|
const Point2f& b = texcoords[1]; |
|
|
|
|
|
|
|
const Point2f& c = texcoords[2]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算边向量
|
|
|
|
|
|
|
|
Point2f v0 = b - a; |
|
|
|
|
|
|
|
Point2f v1 = c - a; |
|
|
|
|
|
|
|
float denom = (v0.x * v0.x + v0.y * v0.y) * (v1.x * v1.x + v1.y * v1.y) - |
|
|
|
|
|
|
|
std::pow(v0.x * v1.x + v0.y * v1.y, 2); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 处理退化三角形情况(面积接近0)
|
|
|
|
|
|
|
|
const float epsilon = 1e-6f; |
|
|
|
|
|
|
|
if (std::abs(denom) < epsilon) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// DEBUG_EXTRA("PointInTriangle - Degenerate triangle, denom=%.10f", denom);
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// DEBUG_EXTRA("PointInTriangle Yes idxFace=%d", idxFace);
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// init last patch to point to a small uniform color patch
|
|
|
|
|
|
|
|
TexturePatch& texturePatch = texturePatches.back(); |
|
|
|
|
|
|
|
const int sizePatch(border*2+1); |
|
|
|
|
|
|
|
texturePatch.rect = cv::Rect(0,0, sizePatch,sizePatch); |
|
|
|
|
|
|
|
for (const FIndex idxFace: texturePatch.faces) { |
|
|
|
|
|
|
|
TexCoord* texcoords = faceTexcoords.data()+idxFace*3; |
|
|
|
|
|
|
|
for (int i=0; i<3; ++i) |
|
|
|
|
|
|
|
texcoords[i] = TexCoord(0.5f, 0.5f); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LOG_OUT() << "GenerateTextureForUV First loop completed" << std::endl; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TD_TIMER_STARTD(); |
|
|
|
|
|
|
|
// perform seam leveling
|
|
|
|
|
|
|
|
if (texturePatches.size() > 2 && (bGlobalSeamLeveling || bLocalSeamLeveling)) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LOG_OUT() << "GenerateTextureForUV 2" << std::endl; |
|
|
|
|
|
|
|
// create seam vertices and edges
|
|
|
|
|
|
|
|
CreateSeamVertices(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LOG_OUT() << "GenerateTextureForUV 3" << std::endl; |
|
|
|
|
|
|
|
// perform global seam leveling
|
|
|
|
|
|
|
|
if (bGlobalSeamLeveling) { |
|
|
|
|
|
|
|
// TD_TIMER_STARTD();
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// GlobalSeamLeveling3();
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// DEBUG_EXTRA("\tglobal seam leveling completed (%s)", TD_TIMER_GET_FMT().c_str());
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LOG_OUT() << "GenerateTextureForUV 4" << std::endl; |
|
|
|
|
|
|
|
// perform local seam leveling
|
|
|
|
|
|
|
|
// if (bLocalSeamLeveling)
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// TD_TIMER_STARTD();
|
|
|
|
|
|
|
|
// LocalSeamLeveling();
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// LocalSeamLeveling3();
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// DEBUG_EXTRA("\tlocal seam leveling completed (%s)", TD_TIMER_GET_FMT().c_str());
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// DEBUG_EXTRA("seam (%s)", TD_TIMER_GET_FMT().c_str());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LOG_OUT() << "GenerateTextureForUV 5" << std::endl; |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// merge texture patches with overlapping rectangles
|
|
|
|
|
|
|
|
for (unsigned i=0; i<texturePatches.size()-1; ++i) { |
|
|
|
|
|
|
|
TexturePatch& texturePatchBig = texturePatches[i]; |
|
|
|
|
|
|
|
for (unsigned j=1; j<texturePatches.size(); ++j) { |
|
|
|
|
|
|
|
if (i == j) |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
TexturePatch& texturePatchSmall = texturePatches[j]; |
|
|
|
|
|
|
|
if (texturePatchBig.label != texturePatchSmall.label) |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
if (!RectsBinPack::IsContainedIn(texturePatchSmall.rect, texturePatchBig.rect)) |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
// translate texture coordinates
|
|
|
|
|
|
|
|
const TexCoord offset(texturePatchSmall.rect.tl()-texturePatchBig.rect.tl()); |
|
|
|
|
|
|
|
for (const FIndex idxFace: texturePatchSmall.faces) { |
|
|
|
|
|
|
|
TexCoord* texcoords = faceTexcoords.data()+idxFace*3; |
|
|
|
|
|
|
|
for (int v=0; v<3; ++v) |
|
|
|
|
|
|
|
texcoords[v] += offset; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// join faces lists
|
|
|
|
|
|
|
|
texturePatchBig.faces.JoinRemove(texturePatchSmall.faces); |
|
|
|
|
|
|
|
// remove the small patch
|
|
|
|
|
|
|
|
texturePatches.RemoveAtMove(j--); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LOG_OUT() << "GenerateTextureForUV 6" << std::endl; |
|
|
|
|
|
|
|
LOG_OUT() << "Second loop completed" << std::endl; |
|
|
|
|
|
|
|
// create texture
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// arrange texture patches to fit the smallest possible texture image
|
|
|
|
|
|
|
|
// const unsigned minPatchSize = 20;
|
|
|
|
|
|
|
|
RectsBinPack::RectWIdxArr unplacedRects(texturePatches.size()); |
|
|
|
|
|
|
|
FOREACH(i, texturePatches) { |
|
|
|
|
|
|
|
if (texturePatches[i].label == NO_ID) { |
|
|
|
|
|
|
|
// 将无效面片区域填充为绿色
|
|
|
|
|
|
|
|
// texturesDiffuse[i](texturePatches[i].rect).setTo(cv::Scalar(0, 255, 0)); // BGR格式,绿色
|
|
|
|
|
|
|
|
// continue;
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// LOG_OUT() << "Third loop completed" << std::endl;
|
|
|
|
|
|
|
|
if (maxTextureSize > 0 && (texturePatches[i].rect.width > maxTextureSize || texturePatches[i].rect.height > maxTextureSize)) { |
|
|
|
|
|
|
|
DEBUG("error: a patch of size %u x %u does not fit the texture", texturePatches[i].rect.width, texturePatches[i].rect.height); |
|
|
|
|
|
|
|
ABORT("the maximum texture size chosen cannot fit a patch"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
unplacedRects[i] = {texturePatches[i].rect, i}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
LOG_OUT() << "unplacedRects loop completed" << std::endl; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LOG_OUT() << "pack patches: one pack per texture file loop completed" << std::endl; |
|
|
|
|
|
|
|
// pack patches: one pack per texture file
|
|
|
|
|
|
|
|
CLISTDEF2IDX(RectsBinPack::RectWIdxArr, TexIndex) placedRects; { |
|
|
|
|
|
|
|
// increase texture size till all patches fit
|
|
|
|
|
|
|
|
// Bruce
|
|
|
|
|
|
|
|
unsigned typeRectsBinPack(nRectPackingHeuristic/100); |
|
|
|
|
|
|
|
unsigned typeSplit((nRectPackingHeuristic-typeRectsBinPack*100)/10); |
|
|
|
|
|
|
|
unsigned typeHeuristic(nRectPackingHeuristic%10); |
|
|
|
|
|
|
|
if (!bOriginFaceview && false) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
typeRectsBinPack = 1; |
|
|
|
|
|
|
|
typeSplit = 0; |
|
|
|
|
|
|
|
typeHeuristic = 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
int textureSize = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
while (!unplacedRects.empty()) { |
|
|
|
|
|
|
|
TD_TIMER_STARTD(); |
|
|
|
|
|
|
|
if (textureSize == 0) { |
|
|
|
|
|
|
|
textureSize = RectsBinPack::ComputeTextureSize(unplacedRects, nTextureSizeMultiple); |
|
|
|
|
|
|
|
if (maxTextureSize > 0 && textureSize > maxTextureSize) |
|
|
|
|
|
|
|
textureSize = maxTextureSize; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
RectsBinPack::RectWIdxArr newPlacedRects; |
|
|
|
|
|
|
|
switch (typeRectsBinPack) { |
|
|
|
|
|
|
|
case 0: { |
|
|
|
|
|
|
|
MaxRectsBinPack pack(textureSize, textureSize); |
|
|
|
|
|
|
|
newPlacedRects = pack.Insert(unplacedRects, (MaxRectsBinPack::FreeRectChoiceHeuristic)typeHeuristic); |
|
|
|
|
|
|
|
break; } |
|
|
|
|
|
|
|
case 1: { |
|
|
|
|
|
|
|
SkylineBinPack pack(textureSize, textureSize, typeSplit!=0); |
|
|
|
|
|
|
|
newPlacedRects = pack.Insert(unplacedRects, (SkylineBinPack::LevelChoiceHeuristic)typeHeuristic); |
|
|
|
|
|
|
|
break; } |
|
|
|
|
|
|
|
case 2: { |
|
|
|
|
|
|
|
GuillotineBinPack pack(textureSize, textureSize); |
|
|
|
|
|
|
|
newPlacedRects = pack.Insert(unplacedRects, false, (GuillotineBinPack::FreeRectChoiceHeuristic)typeHeuristic, (GuillotineBinPack::GuillotineSplitHeuristic)typeSplit); |
|
|
|
|
|
|
|
break; } |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
ABORT("error: unknown RectsBinPack type"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
DEBUG_ULTIMATE("\tpacking texture completed: %u initial patches, %u placed patches, %u texture-size, %u textures (%s)", texturePatches.size(), newPlacedRects.size(), textureSize, placedRects.size(), TD_TIMER_GET_FMT().c_str()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (textureSize == maxTextureSize || unplacedRects.empty()) { |
|
|
|
|
|
|
|
// create texture image
|
|
|
|
|
|
|
|
placedRects.emplace_back(std::move(newPlacedRects)); |
|
|
|
|
|
|
|
// Pixel8U colEmpty2=Pixel8U(0,0,255);
|
|
|
|
|
|
|
|
texturesDiffuse.emplace_back(textureSize, textureSize).setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); |
|
|
|
|
|
|
|
textureSize = 0; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// try again with a bigger texture
|
|
|
|
|
|
|
|
textureSize *= 2; |
|
|
|
|
|
|
|
if (maxTextureSize > 0) |
|
|
|
|
|
|
|
textureSize = std::max(textureSize, maxTextureSize); |
|
|
|
|
|
|
|
unplacedRects.JoinRemove(newPlacedRects); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
LOG_OUT() << "Third loop completed" << std::endl; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LOG_OUT() << "GenerateTextureForUV 7" << std::endl; |
|
|
|
|
|
|
|
Mesh::FaceIdxArr emptyFaceIndexes; |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef TEXOPT_USE_OPENMP |
|
|
|
|
|
|
|
#pragma omp parallel for schedule(dynamic) |
|
|
|
|
|
|
|
for (int_t i=0; i<(int_t)placedRects.size(); ++i) { |
|
|
|
|
|
|
|
for (int_t j=0; j<(int_t)placedRects[(TexIndex)i].size(); ++j) { |
|
|
|
|
|
|
|
const TexIndex idxTexture((TexIndex)i); |
|
|
|
|
|
|
|
const uint32_t idxPlacedPatch((uint32_t)j); |
|
|
|
|
|
|
|
#else |
|
|
|
|
|
|
|
FOREACH(idxTexture, placedRects) { |
|
|
|
|
|
|
|
FOREACH(idxPlacedPatch, placedRects[idxTexture]) { |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
const TexturePatch& texturePatch = texturePatches[placedRects[idxTexture][idxPlacedPatch].patchIdx]; |
|
|
|
|
|
|
|
const RectsBinPack::Rect& rect = placedRects[idxTexture][idxPlacedPatch].rect; |
|
|
|
|
|
|
|
// copy patch image
|
|
|
|
|
|
|
|
ASSERT((rect.width == texturePatch.rect.width && rect.height == texturePatch.rect.height) || |
|
|
|
|
|
|
|
(rect.height == texturePatch.rect.width && rect.width == texturePatch.rect.height)); |
|
|
|
|
|
|
|
int x(0), y(1); |
|
|
|
|
|
|
|
if (texturePatch.label != NO_ID) { |
|
|
|
|
|
|
|
const Image& imageData = images[texturePatch.label]; |
|
|
|
|
|
|
|
cv::Mat patch(imageData.image(texturePatch.rect)); |
|
|
|
|
|
|
|
if (rect.width != texturePatch.rect.width) { |
|
|
|
|
|
|
|
// flip patch and texture-coordinates
|
|
|
|
|
|
|
|
patch = patch.t(); |
|
|
|
|
|
|
|
x = 1; y = 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
patch.copyTo(texturesDiffuse[idxTexture](rect)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
auto it = texturePatch.faces.begin(); |
|
|
|
|
|
|
|
while (it != texturePatch.faces.end()) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
emptyFaceIndexes.push_back(*it); |
|
|
|
|
|
|
|
++it; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// compute final texture coordinates
|
|
|
|
|
|
|
|
const TexCoord offset(rect.tl()); |
|
|
|
|
|
|
|
for (const FIndex idxFace: texturePatch.faces) { |
|
|
|
|
|
|
|
TexCoord* texcoords = faceTexcoords.data()+idxFace*3; |
|
|
|
|
|
|
|
faceTexindices[idxFace] = idxTexture; |
|
|
|
|
|
|
|
for (int v=0; v<3; ++v) { |
|
|
|
|
|
|
|
TexCoord& texcoord = texcoords[v]; |
|
|
|
|
|
|
|
texcoord = TexCoord( |
|
|
|
|
|
|
|
texcoord[x]+offset.x, |
|
|
|
|
|
|
|
texcoord[y]+offset.y |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LOG_OUT() << "GenerateTextureForUV 8" << std::endl; |
|
|
|
|
|
|
|
if (texturesDiffuse.size() == 1) |
|
|
|
|
|
|
|
faceTexindices.Release(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// apply some sharpening
|
|
|
|
|
|
|
|
if (fSharpnessWeight > 0) { |
|
|
|
|
|
|
|
constexpr double sigma = 1.5; |
|
|
|
|
|
|
|
for (auto &textureDiffuse: texturesDiffuse) { |
|
|
|
|
|
|
|
Image8U3 blurryTextureDiffuse; |
|
|
|
|
|
|
|
cv::GaussianBlur(textureDiffuse, blurryTextureDiffuse, cv::Size(), sigma); |
|
|
|
|
|
|
|
cv::addWeighted(textureDiffuse, 1+fSharpnessWeight, blurryTextureDiffuse, -fSharpnessWeight, 0, textureDiffuse); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
LOG_OUT() << "Fourth loop completed" << std::endl; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::ofstream out(basename + "_empty_color_triangles.txt"); |
|
|
|
|
|
|
|
RFOREACHPTR(pIdxF, emptyFaceIndexes) { |
|
|
|
|
|
|
|
out << *pIdxF << "\n"; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
out.close(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void MeshTexture::GlobalSeamLevelingExternalUV() |
|
|
|
void MeshTexture::GlobalSeamLevelingExternalUV() |
|
|
|
{ |
|
|
|
{ |
|
|
|
@ -9574,6 +9940,7 @@ bool SaveGeneratedTextures(const Mesh::Image8U3Arr& generatedTextures, const std |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//*
|
|
|
|
bool MeshTexture::TextureWithExistingUV(const IIndexArr& views, int nIgnoreMaskLabel, float fOutlierThreshold, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight) |
|
|
|
bool MeshTexture::TextureWithExistingUV(const IIndexArr& views, int nIgnoreMaskLabel, float fOutlierThreshold, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight) |
|
|
|
{ |
|
|
|
{ |
|
|
|
DEBUG_EXTRA("TextureWithExistingUV 1"); |
|
|
|
DEBUG_EXTRA("TextureWithExistingUV 1"); |
|
|
|
@ -9646,6 +10013,92 @@ bool MeshTexture::TextureWithExistingUV(const IIndexArr& views, int nIgnoreMaskL |
|
|
|
DEBUG_EXTRA("纹理生成失败"); |
|
|
|
DEBUG_EXTRA("纹理生成失败"); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
bool MeshTexture::TextureWithExistingUV(const IIndexArr& views, int nIgnoreMaskLabel, float fOutlierThreshold, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
DEBUG_EXTRA("TextureWithExistingUV 1 - 从已生成纹理采样"); |
|
|
|
|
|
|
|
TD_TIMER_START(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 验证输入
|
|
|
|
|
|
|
|
if (scene.mesh.faceTexcoords.empty()) { |
|
|
|
|
|
|
|
VERBOSE("error: mesh does not contain UV coordinates"); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (scene.mesh.faceTexcoords.size() != scene.mesh.faces.size() * 3) { |
|
|
|
|
|
|
|
VERBOSE("error: UV coordinates count does not match face count, %d, %d", scene.mesh.faceTexcoords.size(), scene.mesh.faces.size() * 3); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否已经有生成的纹理
|
|
|
|
|
|
|
|
if (!scene.mesh.texturesDiffuse.empty() && !scene.mesh.faceTexindices.empty()) { |
|
|
|
|
|
|
|
DEBUG_EXTRA("TextureWithExistingUV: 使用已生成的纹理,数量: %zu", scene.mesh.texturesDiffuse.size()); |
|
|
|
|
|
|
|
return true; // 已经通过 GenerateTextureForUV 生成了纹理,直接返回成功
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 如果没有已生成的纹理,重新从图像投影生成
|
|
|
|
|
|
|
|
DEBUG_EXTRA("TextureWithExistingUV 2 - 从图像重新生成纹理"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 为每个面选择最佳视图
|
|
|
|
|
|
|
|
FaceDataViewArr facesDatas; |
|
|
|
|
|
|
|
if (!ListCameraFaces(facesDatas, fOutlierThreshold, nIgnoreMaskLabel, views, false)) { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("TextureWithExistingUV 3 faceSize=%d", scene.mesh.faces.size()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 为每个面分配最佳视图
|
|
|
|
|
|
|
|
LabelArr faceLabels(scene.mesh.faces.size()); |
|
|
|
|
|
|
|
FOREACH(idxFace, scene.mesh.faces) { |
|
|
|
|
|
|
|
const FaceDataArr& faceDatas = facesDatas[idxFace]; |
|
|
|
|
|
|
|
if (faceDatas.empty()) { |
|
|
|
|
|
|
|
faceLabels[idxFace] = 0; // 无视图可用
|
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 选择质量最高的视图
|
|
|
|
|
|
|
|
float bestQuality = -1; |
|
|
|
|
|
|
|
IIndex bestView = NO_ID; |
|
|
|
|
|
|
|
for (const FaceData& data : faceDatas) { |
|
|
|
|
|
|
|
if (data.quality > bestQuality && !data.bInvalidFacesRelative) { |
|
|
|
|
|
|
|
bestQuality = data.quality; |
|
|
|
|
|
|
|
bestView = data.idxView; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
faceLabels[idxFace] = (bestView != NO_ID) ? (bestView + 1) : 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 从现有纹理采样生成纹理图集
|
|
|
|
|
|
|
|
Mesh::Image8U3Arr generatedTextures = SampleFromExistingTextures(faceLabels, views, nTextureSizeMultiple, colEmpty, fSharpnessWeight); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!generatedTextures.empty()) { |
|
|
|
|
|
|
|
scene.mesh.texturesDiffuse = std::move(generatedTextures); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 同时设置面的纹理索引
|
|
|
|
|
|
|
|
scene.mesh.faceTexindices.resize(scene.mesh.faces.size()); |
|
|
|
|
|
|
|
for (size_t i = 0; i < faceLabels.size(); ++i) { |
|
|
|
|
|
|
|
// 假设所有面都在第一个纹理中
|
|
|
|
|
|
|
|
scene.mesh.faceTexindices[i] = 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("成功生成 %zu 个纹理,已赋值给 mesh", scene.mesh.texturesDiffuse.size()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 🆕 保存纹理到文件
|
|
|
|
|
|
|
|
std::string outputDir = "texture_output"; |
|
|
|
|
|
|
|
if (SaveGeneratedTextures(scene.mesh.texturesDiffuse, outputDir)) { |
|
|
|
|
|
|
|
DEBUG_EXTRA("纹理已成功保存到目录: %s", outputDir.c_str()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("纹理生成失败"); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
Point2f MeshTexture::ProjectPointWithAutoCorrection(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage) { |
|
|
|
Point2f MeshTexture::ProjectPointWithAutoCorrection(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage) { |
|
|
|
Point2f imgPoint = camera.ProjectPointP(worldPoint); |
|
|
|
Point2f imgPoint = camera.ProjectPointP(worldPoint); |
|
|
|
@ -10151,6 +10604,15 @@ void FillTextureGaps(Image8U3& textureAtlas, const Mesh::TexCoordArr& faceTexcoo |
|
|
|
Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasFromUV(const LabelArr& faceLabels, const IIndexArr& views, |
|
|
|
Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasFromUV(const LabelArr& faceLabels, const IIndexArr& views, |
|
|
|
unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight) |
|
|
|
unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
// 如果已经有生成的纹理,直接返回
|
|
|
|
|
|
|
|
if (!scene.mesh.texturesDiffuse.empty()) { |
|
|
|
|
|
|
|
DEBUG_EXTRA("GenerateTextureAtlasFromUV: 使用已生成的纹理,数量: %zu", scene.mesh.texturesDiffuse.size()); |
|
|
|
|
|
|
|
return scene.mesh.texturesDiffuse; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 否则,重新生成纹理
|
|
|
|
|
|
|
|
DEBUG_EXTRA("GenerateTextureAtlasFromUV: 从图像重新生成纹理"); |
|
|
|
|
|
|
|
|
|
|
|
// 1. 分析整个模型的UV布局
|
|
|
|
// 1. 分析整个模型的UV布局
|
|
|
|
AABB2f uvBounds(true); |
|
|
|
AABB2f uvBounds(true); |
|
|
|
FOREACH(i, scene.mesh.faceTexcoords) { |
|
|
|
FOREACH(i, scene.mesh.faceTexcoords) { |
|
|
|
@ -10308,7 +10770,7 @@ Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasFromUV(const LabelArr& faceLa |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 5. 添加后处理填充函数
|
|
|
|
// 5. 添加后处理填充函数
|
|
|
|
FillTextureGaps(textureAtlas, scene.mesh.faceTexcoords, (FIndex)scene.mesh.faces.size(), faceLabels, textureSize, colEmpty); |
|
|
|
FillTextureGaps(textureAtlas, scene.mesh.faceTexcoords, (FIndex)scene.mesh.faces.size(), faceLabels, textureSize, colEmpty); |
|
|
|
|
|
|
|
|
|
|
|
// 6. 应用后处理
|
|
|
|
// 6. 应用后处理
|
|
|
|
if (fSharpnessWeight > 0) { |
|
|
|
if (fSharpnessWeight > 0) { |
|
|
|
@ -10321,6 +10783,146 @@ Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasFromUV(const LabelArr& faceLa |
|
|
|
return textures; |
|
|
|
return textures; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Mesh::Image8U3Arr MeshTexture::SampleFromExistingTextures(const LabelArr& faceLabels, const IIndexArr& views, |
|
|
|
|
|
|
|
unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
DEBUG_EXTRA("SampleFromExistingTextures: 从已生成纹理采样"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 检查是否有已生成的纹理
|
|
|
|
|
|
|
|
if (scene.mesh.texturesDiffuse.empty() || scene.mesh.faceTexindices.empty()) { |
|
|
|
|
|
|
|
DEBUG_EXTRA("SampleFromExistingTextures: 没有已生成的纹理,回退到 GenerateTextureAtlasFromUV"); |
|
|
|
|
|
|
|
return GenerateTextureAtlasFromUV(faceLabels, views, nTextureSizeMultiple, colEmpty, fSharpnessWeight); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 分析整个模型的UV布局
|
|
|
|
|
|
|
|
AABB2f uvBounds(true); |
|
|
|
|
|
|
|
FOREACH(i, scene.mesh.faceTexcoords) { |
|
|
|
|
|
|
|
const TexCoord& uv = scene.mesh.faceTexcoords[i]; |
|
|
|
|
|
|
|
uvBounds.InsertFull(uv); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 根据UV范围确定纹理图集尺寸
|
|
|
|
|
|
|
|
const int textureSize = ComputeOptimalTextureSize(uvBounds.ptMin, uvBounds.ptMax, nTextureSizeMultiple); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 创建目标纹理图集
|
|
|
|
|
|
|
|
Mesh::Image8U3Arr textures; |
|
|
|
|
|
|
|
Image8U3& textureAtlas = textures.emplace_back(textureSize, textureSize); |
|
|
|
|
|
|
|
textureAtlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("从已生成纹理采样: 创建纹理图集 %dx%d", textureSize, textureSize); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 为每个面片从现有纹理采样
|
|
|
|
|
|
|
|
#ifdef _USE_OPENMP |
|
|
|
|
|
|
|
#pragma omp parallel for schedule(dynamic) |
|
|
|
|
|
|
|
for (int_t idxFace = 0; idxFace < (int_t)scene.mesh.faces.size(); ++idxFace) { |
|
|
|
|
|
|
|
#else |
|
|
|
|
|
|
|
FOREACH(idxFace, scene.mesh.faces) { |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
const FIndex faceID = (FIndex)idxFace; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取面的纹理索引
|
|
|
|
|
|
|
|
if (faceID >= scene.mesh.faceTexindices.size()) { |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TexIndex texIdx = scene.mesh.faceTexindices[faceID]; |
|
|
|
|
|
|
|
if (texIdx >= scene.mesh.texturesDiffuse.size()) { |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取源纹理和目标纹理
|
|
|
|
|
|
|
|
const Image8U3& sourceTexture = scene.mesh.texturesDiffuse[texIdx]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取面的UV坐标
|
|
|
|
|
|
|
|
const TexCoord* uvCoords = &scene.mesh.faceTexcoords[faceID * 3]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算面片在纹理图集中的边界框
|
|
|
|
|
|
|
|
AABB2f faceUVBounds(true); |
|
|
|
|
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
|
|
|
|
faceUVBounds.InsertFull(uvCoords[i]); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 将UV坐标转换到纹理像素坐标
|
|
|
|
|
|
|
|
const int startX = std::max(0, (int)(faceUVBounds.ptMin.x() * textureSize)); |
|
|
|
|
|
|
|
const int startY = std::max(0, (int)(faceUVBounds.ptMin.y() * textureSize)); |
|
|
|
|
|
|
|
const int endX = std::min(textureSize - 1, (int)(faceUVBounds.ptMax.x() * textureSize)); |
|
|
|
|
|
|
|
const int endY = std::min(textureSize - 1, (int)(faceUVBounds.ptMax.y() * textureSize)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 对面片覆盖的每个纹理像素进行采样
|
|
|
|
|
|
|
|
for (int y = startY; y <= endY; ++y) { |
|
|
|
|
|
|
|
for (int x = startX; x <= endX; ++x) { |
|
|
|
|
|
|
|
const Point2f texCoord((float)x / textureSize, (float)y / textureSize); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否在三角形内
|
|
|
|
|
|
|
|
Point3f barycentric; |
|
|
|
|
|
|
|
if (PointInTriangle(texCoord, uvCoords[0], uvCoords[1], uvCoords[2], barycentric)) { |
|
|
|
|
|
|
|
// 从源纹理采样颜色
|
|
|
|
|
|
|
|
Pixel8U sampledColor = SampleTextureBilinear(sourceTexture, texCoord); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 将采样颜色写入纹理图集
|
|
|
|
|
|
|
|
#ifdef _USE_OPENMP |
|
|
|
|
|
|
|
#pragma omp critical |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
textureAtlas(y, x) = sampledColor; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 应用后处理
|
|
|
|
|
|
|
|
if (fSharpnessWeight > 0) { |
|
|
|
|
|
|
|
ApplySharpening(textureAtlas, fSharpnessWeight); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("从已生成纹理采样完成: %u个面片, 纹理尺寸%dx%d", |
|
|
|
|
|
|
|
scene.mesh.faces.size(), textureSize, textureSize); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return textures; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 辅助函数:从纹理中双线性采样
|
|
|
|
|
|
|
|
Pixel8U MeshTexture::SampleTextureBilinear(const Image8U3& texture, const Point2f& texCoord) { |
|
|
|
|
|
|
|
const float u = texCoord.x * texture.cols; |
|
|
|
|
|
|
|
const float v = texCoord.y * texture.rows; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const int x = (int)u; |
|
|
|
|
|
|
|
const int y = (int)v; |
|
|
|
|
|
|
|
const float dx = u - x; |
|
|
|
|
|
|
|
const float dy = v - y; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const int x1 = std::min(x, texture.cols - 1); |
|
|
|
|
|
|
|
const int y1 = std::min(y, texture.rows - 1); |
|
|
|
|
|
|
|
const int x2 = std::min(x + 1, texture.cols - 1); |
|
|
|
|
|
|
|
const int y2 = std::min(y + 1, texture.rows - 1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const Pixel8U& p11 = texture(y1, x1); |
|
|
|
|
|
|
|
const Pixel8U& p12 = texture(y2, x1); |
|
|
|
|
|
|
|
const Pixel8U& p21 = texture(y1, x2); |
|
|
|
|
|
|
|
const Pixel8U& p22 = texture(y2, x2); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 双线性插值
|
|
|
|
|
|
|
|
Pixel8U result; |
|
|
|
|
|
|
|
for (int c = 0; c < 3; ++c) { |
|
|
|
|
|
|
|
const float a = p11[c] * (1 - dx) + p21[c] * dx; |
|
|
|
|
|
|
|
const float b = p12[c] * (1 - dx) + p22[c] * dx; |
|
|
|
|
|
|
|
result[c] = (uint8_t)(a * (1 - dy) + b * dy); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 辅助函数:应用锐化
|
|
|
|
|
|
|
|
void MeshTexture::ApplySharpening(Image8U3& texture, float fSharpnessWeight) { |
|
|
|
|
|
|
|
if (fSharpnessWeight <= 0) return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constexpr double sigma = 1.5; |
|
|
|
|
|
|
|
Image8U3 blurryTexture; |
|
|
|
|
|
|
|
cv::GaussianBlur(texture, blurryTexture, cv::Size(), sigma); |
|
|
|
|
|
|
|
cv::addWeighted(texture, 1 + fSharpnessWeight, blurryTexture, -fSharpnessWeight, 0, texture); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool MeshTexture::ValidateProjection(const Vertex& worldPoint, |
|
|
|
bool MeshTexture::ValidateProjection(const Vertex& worldPoint, |
|
|
|
const Image& sourceImage, Point2f imgPoint, |
|
|
|
const Image& sourceImage, Point2f imgPoint, |
|
|
|
@ -10877,39 +11479,6 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi |
|
|
|
DEBUG_EXTRA("First pass (virtual faces) completed: %u faces (%s)", mesh.faces.size(), TD_TIMER_GET_FMT().c_str()); |
|
|
|
DEBUG_EXTRA("First pass (virtual faces) completed: %u faces (%s)", mesh.faces.size(), TD_TIMER_GET_FMT().c_str()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 第二轮处理:专门处理无效面片(非虚拟面映射)
|
|
|
|
|
|
|
|
if (false) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
TD_TIMER_STARTD(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 收集所有无效面片的索引
|
|
|
|
|
|
|
|
Mesh::FaceIdxArr invalidFaces; |
|
|
|
|
|
|
|
for (FIndex fid = 0; fid < mesh.faces.size(); ++fid) { |
|
|
|
|
|
|
|
if (texture.scene.mesh.invalidFaces.data.contains(fid)) { |
|
|
|
|
|
|
|
invalidFaces.push_back(fid); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!invalidFaces.empty()) { |
|
|
|
|
|
|
|
// 创建新的纹理处理器,专门处理无效面片
|
|
|
|
|
|
|
|
MeshTexture textureInvalid(*this, nResolutionLevel, nMinResolution); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 使用非虚拟面模式处理无效面片
|
|
|
|
|
|
|
|
if (!textureInvalid.FaceViewSelection4(0, fOutlierThreshold, fRatioDataSmoothness, |
|
|
|
|
|
|
|
nIgnoreMaskLabel, views, &invalidFaces)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 合并两轮处理的结果
|
|
|
|
|
|
|
|
texture.texturePatches.Join(textureInvalid.texturePatches); |
|
|
|
|
|
|
|
texture.seamEdges.Join(textureInvalid.seamEdges); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Second pass (invalid faces) completed: %u faces (%s)", |
|
|
|
|
|
|
|
invalidFaces.size(), TD_TIMER_GET_FMT().c_str()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
DEBUG_EXTRA("Assigning the best view to each face completed: %u faces (%s)", mesh.faces.size(), TD_TIMER_GET_FMT().c_str()); |
|
|
|
DEBUG_EXTRA("Assigning the best view to each face completed: %u faces (%s)", mesh.faces.size(), TD_TIMER_GET_FMT().c_str()); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -10934,6 +11503,9 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
texture.GenerateTextureForUV(bGlobalSeamLeveling, bLocalSeamLeveling, nTextureSizeMultiple, nRectPackingHeuristic, colEmpty, fSharpnessWeight, maxTextureSize, baseFileName, bOriginFaceview, this); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 使用新的纹理生成方法
|
|
|
|
// 使用新的纹理生成方法
|
|
|
|
MeshTexture texture(*this, nResolutionLevel, nMinResolution); |
|
|
|
MeshTexture texture(*this, nResolutionLevel, nMinResolution); |
|
|
|
if (!texture.TextureWithExistingUV(views, nIgnoreMaskLabel, fOutlierThreshold, nTextureSizeMultiple, colEmpty, fSharpnessWeight)){ |
|
|
|
if (!texture.TextureWithExistingUV(views, nIgnoreMaskLabel, fOutlierThreshold, nTextureSizeMultiple, colEmpty, fSharpnessWeight)){ |
|
|
|
@ -10947,6 +11519,7 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi |
|
|
|
// mesh.CheckUVValid();
|
|
|
|
// mesh.CheckUVValid();
|
|
|
|
|
|
|
|
|
|
|
|
// generate the texture image and atlas
|
|
|
|
// generate the texture image and atlas
|
|
|
|
|
|
|
|
if (false) |
|
|
|
{ |
|
|
|
{ |
|
|
|
TD_TIMER_STARTD(); |
|
|
|
TD_TIMER_STARTD(); |
|
|
|
texture.GenerateTexture(bGlobalSeamLeveling, bLocalSeamLeveling, nTextureSizeMultiple, nRectPackingHeuristic, colEmpty, fSharpnessWeight, maxTextureSize, baseFileName, bOriginFaceview, this); |
|
|
|
texture.GenerateTexture(bGlobalSeamLeveling, bLocalSeamLeveling, nTextureSizeMultiple, nRectPackingHeuristic, colEmpty, fSharpnessWeight, maxTextureSize, baseFileName, bOriginFaceview, this); |
|
|
|
|