From 0dceffb66f566063de11d9fe9303f436afbf61f7 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Wed, 22 Apr 2026 17:04:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=AD=E9=97=B4=E6=9C=AA=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 2150 ++++++++++++++++++++++++++++++++++--- 1 file changed, 1971 insertions(+), 179 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index f0c2c65..63e4cf5 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -166,6 +166,28 @@ enum Mask { interior = 255 }; +// 视图选择数据结构 +struct ViewSelectionData { + int viewID; // 视图ID + float quality; // 视图质量评分 + float consistency; // 与相邻面的视图一致性 + cv::Rect patchBounds; // 纹理块边界 + + ViewSelectionData() : viewID(-1), quality(0.0f), consistency(0.0f) {} + ViewSelectionData(int id, float q, float c) : + viewID(id), quality(q), consistency(c) {} +}; + +// 纹理块质量信息 +struct PatchQualityInfo { + float averageQuality; + float minQuality; + float maxQuality; + std::vector faceQualities; +}; + +std::vector patchQualityInfos; + struct MeshTexture { // used to render the surface to a view camera typedef TImage FaceMap; @@ -492,6 +514,60 @@ public: const Mesh::TexCoordArr& existingTexcoords, // 添加已有UV参数 const Mesh::TexIndexArr& existingTexindices // 添加已有纹理索引参数 ); + bool GenerateTextureWithViewConsistency( + bool bGlobalSeamLeveling, bool bLocalSeamLeveling, + unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, + Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, + const String& basename, bool bOriginFaceview, Scene* pScene); + // 在头文件中,修改函数声明: + bool PackTextureAtlases( + Mesh::TexCoordArr& faceTexcoords2, + Mesh::TexIndexArr& faceTexindices2, + std::vector& generatedTextures, + unsigned nTextureSizeMultiple, + unsigned nRectPackingHeuristic, + Pixel8U colEmpty, + int maxTextureSize); + void SelectOptimalViewsWithConsistency( + std::vector& faceViewData, + int minPatchSize); + void CreateConsistentTexturePatches( + const std::vector& faceViewData, + std::vector& patchAssignments, + int minPatchSize); + void GenerateHighQualityTexture( + std::vector& textures, + const Mesh::TexCoordArr& faceTexcoords, + const Mesh::TexIndexArr& faceTexindices, + float fSharpnessWeight, + Pixel8U colEmpty); + cv::Vec3f SampleHighQuality(const cv::Mat& image, const Point2f& point); + void FillMissingPixelsWithViewConsistency( + Image8U3& texture, + cv::Mat3f& colorAccum, + cv::Mat1f& weightAccum, + cv::Mat1i& viewAccum, + Pixel8U colEmpty); + float ComputeViewQuality(FIndex idxFace, int viewID); + float ComputeFaceDistance(FIndex fid1, FIndex fid2); + + cv::Rect ComputeOptimalPatchBounds(const AABB2f& aabb, const cv::Size& imageSize, int border); + void GlobalSeamLevelingEnhanced(); + void LocalSeamLevelingEnhanced(); + void MergeOverlappingPatches(Mesh::TexCoordArr& faceTexcoords2); + void PackTexturePatches(const Mesh::TexCoordArr& faceTexcoords2, + const Mesh::TexIndexArr& faceTexindices2, + std::vector& generatedTextures, + unsigned nTextureSizeMultiple, + unsigned nRectPackingHeuristic, + int maxTextureSize); + void ApplyAdaptiveSharpening(std::vector& textures, float fSharpnessWeight); + void FillTextureHoles(std::vector& textures, Pixel8U colEmpty); + bool IsFaceVisibleFromView(FIndex idxFace, int viewID); + cv::Mat EnhanceTextureQuality(const cv::Mat& texture, const std::vector& faceQualities); + void OptimizeTextureSeams(const std::vector& textures, + const std::vector>& seamPoints); + void CheckColorChannels(const Image8U3& texture, const std::string& name); Mesh::Image8U3Arr GenerateTextureAtlasWith3DBridge( const LabelArr& faceLabels, @@ -9975,229 +10051,1938 @@ void MeshTexture::CheckColorChannels(const Image8U3& texture, const std::string& DEBUG_EXTRA("%s 平均颜色: B=%.1f, G=%.1f, R=%.1f", name.c_str(), mean[0], mean[1], mean[2]); } - -bool MeshTexture::TextureWithExistingUV( - const IIndexArr& views, - int nIgnoreMaskLabel, - float fOutlierThreshold, - unsigned nTextureSizeMultiple, - Pixel8U colEmpty, - float fSharpnessWeight, - const Mesh::Image8U3Arr& existingTextures, - const Mesh::TexCoordArr& existingTexcoords, - const Mesh::TexIndexArr& existingTexindices) +bool MeshTexture::PackTextureAtlases( + Mesh::TexCoordArr& faceTexcoords2, + Mesh::TexIndexArr& faceTexindices2, + std::vector& generatedTextures, + unsigned nTextureSizeMultiple, + unsigned nRectPackingHeuristic, + Pixel8U colEmpty, + int maxTextureSize) { - DEBUG_EXTRA("TextureWithExistingUV - 使用3D几何坐标作为桥梁"); - TD_TIMER_START(); + DEBUG_EXTRA("PackTextureAtlases: heuristic=%u, maxSize=%d", nRectPackingHeuristic, maxTextureSize); - // 1. 验证输入 - if (scene.mesh.faceTexcoords.empty()) { - VERBOSE("error: mesh does not contain UV coordinates"); + if (texturePatches.empty()) { + DEBUG_EXTRA("No texture patches to pack"); return false; } - if (existingTextures.empty()) { - VERBOSE("error: no existing texture data provided"); + // 1. 准备纹理块列表 + std::vector patches; + for (auto& patch : texturePatches) { + if (patch.rect.width > 0 && patch.rect.height > 0) { + patches.push_back(&patch); + } + } + + if (patches.empty()) { + DEBUG_EXTRA("No valid texture patches to pack"); return false; } - DEBUG_EXTRA("Processing %zu faces with existing texture data", scene.mesh.faces.size()); + DEBUG_EXTRA("Packing %zu texture patches", patches.size()); - // 2. 为每个面选择最佳视图(从原始图像,而不是已有纹理) - FaceDataViewArr facesDatas; - if (!ListCameraFaces(facesDatas, fOutlierThreshold, nIgnoreMaskLabel, views, false)) { - return false; + // 2. 计算图集大小 + int atlasSize = 1024; // 默认大小 + if (maxTextureSize > 0) { + atlasSize = std::min(atlasSize, maxTextureSize); } - // 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; + // 确保大小是 nTextureSizeMultiple 的倍数 + if (nTextureSizeMultiple > 1) { + atlasSize = ((atlasSize + nTextureSizeMultiple - 1) / nTextureSizeMultiple) * nTextureSizeMultiple; } - // 4. 生成纹理图集 - Mesh::Image8U3Arr generatedTextures = GenerateTextureAtlasWith3DBridge( - faceLabels, views, existingTextures, existingTexcoords, existingTexindices, - nTextureSizeMultiple, colEmpty, fSharpnessWeight - ); + DEBUG_EXTRA("Using atlas size: %dx%d", atlasSize, atlasSize); - if (!generatedTextures.empty()) { - // 检查颜色通道 - CheckColorChannels(generatedTextures[0], "生成的纹理"); + // 3. 根据启发式算法对纹理块排序 + switch (nRectPackingHeuristic) { + case 0: // 按面积降序 + std::sort(patches.begin(), patches.end(), + [](const TexturePatch* a, const TexturePatch* b) { + return a->rect.area() > b->rect.area(); + }); + break; + case 1: // 按宽度降序 + std::sort(patches.begin(), patches.end(), + [](const TexturePatch* a, const TexturePatch* b) { + return a->rect.width > b->rect.width; + }); + break; + case 2: // 按高度降序 + std::sort(patches.begin(), patches.end(), + [](const TexturePatch* a, const TexturePatch* b) { + return a->rect.height > b->rect.height; + }); + break; + case 3: // 按最大边降序 + std::sort(patches.begin(), patches.end(), + [](const TexturePatch* a, const TexturePatch* b) { + return std::max(a->rect.width, a->rect.height) > + std::max(b->rect.width, b->rect.height); + }); + break; + default: + // 不排序 + break; + } + + // 4. 使用Skyline算法进行打包 + std::vector skyline(atlasSize, 0); // Skyline高度数组 + std::vector> placements; // 存放每个patch的位置 + + // 创建第一个图集 + Image8U3 firstAtlas(atlasSize, atlasSize); + firstAtlas.fill(colEmpty); + generatedTextures.push_back(firstAtlas); + + int currentAtlasIndex = 0; // 当前图集索引 + + for (const auto& patch : patches) { + int bestX = -1; + int bestY = INT_MAX; + int bestWidth = patch->rect.width; + int bestHeight = patch->rect.height; - // 检查源纹理颜色通道 - if (!existingTextures.empty()) { - CheckColorChannels(existingTextures[0], "源纹理"); + // 寻找最佳放置位置 + for (int x = 0; x <= atlasSize - bestWidth; ++x) { + int maxY = 0; + for (int w = 0; w < bestWidth; ++w) { + maxY = std::max(maxY, skyline[x + w]); + } + + if (maxY + bestHeight <= atlasSize && maxY < bestY) { + bestY = maxY; + bestX = x; + } } - // 保存纹理 - scene.mesh.texturesDiffuse = std::move(generatedTextures); - - // 设置面的纹理索引 - if (scene.mesh.texturesDiffuse.size() > 1) { - scene.mesh.faceTexindices.resize(scene.mesh.faces.size()); - for (size_t i = 0; i < scene.mesh.faces.size(); ++i) { - scene.mesh.faceTexindices[i] = 0; + if (bestX != -1) { + // 找到位置,放置纹理块 + placements.emplace_back(bestX, bestY); + + // 更新skyline + for (int w = 0; w < bestWidth; ++w) { + skyline[bestX + w] = bestY + bestHeight; + } + + // 更新纹理坐标 + for (FIndex fid : patch->faces) { + TexCoord* texcoords = faceTexcoords2.data() + fid * 3; + + for (int j = 0; j < 3; ++j) { + // 从原始坐标转换到图集坐标 + float u = (texcoords[j].x - patch->rect.x) / patch->rect.width; + float v = (texcoords[j].y - patch->rect.y) / patch->rect.height; + + // 计算新的图集坐标 + texcoords[j].x = (bestX + u * bestWidth) / atlasSize; + texcoords[j].y = (bestY + v * bestHeight) / atlasSize; + } + + // 更新面的纹理索引 + faceTexindices2[fid] = currentAtlasIndex; } } else { - scene.mesh.faceTexindices.Release(); + // 当前图集已满,创建新图集 + DEBUG_EXTRA("Current atlas is full, creating new one"); + + // 创建新图集 + Image8U3 newAtlas(atlasSize, atlasSize); + newAtlas.fill(colEmpty); + generatedTextures.push_back(newAtlas); + currentAtlasIndex++; + + // 重置skyline + std::fill(skyline.begin(), skyline.end(), 0); + + // 重新放置当前纹理块 + int x = 0; + int y = 0; + placements.emplace_back(x, y); + + // 更新skyline + for (int w = 0; w < bestWidth; ++w) { + skyline[x + w] = bestHeight; + } + + // 更新纹理坐标 + for (FIndex fid : patch->faces) { + TexCoord* texcoords = faceTexcoords2.data() + fid * 3; + + for (int j = 0; j < 3; ++j) { + // 从原始坐标转换到图集坐标 + float u = (texcoords[j].x - patch->rect.x) / patch->rect.width; + float v = (texcoords[j].y - patch->rect.y) / patch->rect.height; + + // 计算新的图集坐标 + texcoords[j].x = (x + u * bestWidth) / atlasSize; + texcoords[j].y = (y + v * bestHeight) / atlasSize; + } + + // 更新面的纹理索引 + faceTexindices2[fid] = currentAtlasIndex; + } } - - DEBUG_EXTRA("Generated %zu textures from existing data", - scene.mesh.texturesDiffuse.size()); - return true; } - DEBUG_EXTRA("Texture generation failed"); - return false; + DEBUG_EXTRA("Successfully packed %zu patches into %zu texture atlases", + patches.size(), generatedTextures.size()); + + return true; } -Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasWith3DBridge( - const LabelArr& faceLabels, - const IIndexArr& views, - const Mesh::Image8U3Arr& sourceTextures, - const Mesh::TexCoordArr& sourceTexcoords, - const Mesh::TexIndexArr& sourceTexindices, - unsigned nTextureSizeMultiple, - Pixel8U colEmpty, - float fSharpnessWeight) +// 主要改进的纹理映射函数 +bool MeshTexture::GenerateTextureWithViewConsistency( + bool bGlobalSeamLeveling, bool bLocalSeamLeveling, + unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, + Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, + const String& basename, bool bOriginFaceview, Scene* pScene) { - DEBUG_EXTRA("GenerateTextureAtlasWith3DBridge - 使用3D几何坐标作为桥梁"); + DEBUG_EXTRA("Starting texture generation with view consistency optimization"); + TD_TIMER_START(); - // 1. 分析外部UV布局 - AABB2f uvBounds(true); - FOREACH(i, scene.mesh.faceTexcoords) { - const TexCoord& uv = scene.mesh.faceTexcoords[i]; - uvBounds.InsertFull(uv); + const int border = (bOriginFaceview) ? 2 : 4; + const int minPatchSize = 50; // 最小纹理块大小 + + // 1. 创建视图一致性映射 + std::vector faceViewData(faces.size()); + std::vector patchAssignments(faces.size(), -1); + + // 2. 为每个面选择最佳视图(考虑相邻面一致性) + SelectOptimalViewsWithConsistency(faceViewData, minPatchSize); + + // 3. 基于视图一致性创建纹理块 + CreateConsistentTexturePatches(faceViewData, patchAssignments, minPatchSize); + + // 4. 生成纹理坐标 + Mesh::TexCoordArr faceTexcoords2(faces.size() * 3); + Mesh::TexIndexArr faceTexindices2(faces.size()); + + #ifdef TEXOPT_USE_OPENMP + #pragma omp parallel for schedule(dynamic) + for (int_t idx = 0; idx < (int_t)texturePatches.size() - 1; ++idx) { + #else + for (uint32_t idx = 0; idx < texturePatches.size() - 1; ++idx) { + #endif + TexturePatch& texturePatch = texturePatches[idx]; + const Image& imageData = images[texturePatch.label]; + AABB2f aabb(true); + + // 计算纹理块的UV边界 + for (const FIndex idxFace : texturePatch.faces) { + const Face& face = faces[idxFace]; + TexCoord* texcoords = faceTexcoords2.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]); + } + } + + // 设置纹理块边界 + texturePatch.rect = ComputeOptimalPatchBounds(aabb, imageData.image.size(), border); } - // 确保UV在[0,1]范围内 - if (uvBounds.ptMin.x() < 0 || uvBounds.ptMin.y() < 0 || - uvBounds.ptMax.x() > 1 || uvBounds.ptMax.y() > 1) { - DEBUG_EXTRA("UV超出[0,1]范围,进行归一化"); - uvBounds = AABB2f(true); - for (size_t i = 0; i < scene.mesh.faceTexcoords.size(); i += 3) { - for (int v = 0; v < 3; ++v) { - const TexCoord& uv = scene.mesh.faceTexcoords[i + v]; - uvBounds.InsertFull(uv); + // 5. 处理剩余的"无效视图"面(分配到默认纹理块) + if (!texturePatches.empty() && texturePatches.back().label == NO_ID) { + 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 = faceTexcoords2.data() + idxFace * 3; + for (int i = 0; i < 3; ++i) { + texcoords[i] = TexCoord(0.5f, 0.5f); } } } - // 计算纹理尺寸 - const float uvWidth = uvBounds.ptMax.x() - uvBounds.ptMin.x(); - const float uvHeight = uvBounds.ptMax.y() - uvBounds.ptMin.y(); - const int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple); + // 6. 执行接缝均衡 + if (texturePatches.size() > 2) { + CreateSeamVertices(); + + if (bGlobalSeamLeveling) { + GlobalSeamLevelingEnhanced(); + } + + if (bLocalSeamLeveling) { + LocalSeamLevelingEnhanced(); + } + } -// 创建目标纹理 - Mesh::Image8U3Arr textures; - Image8U3& textureAtlas = textures.emplace_back(textureSize, textureSize); - // 注意:cv::Scalar 使用 BGR 顺序 - textureAtlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); + // 7. 合并重叠的纹理块 + MergeOverlappingPatches(faceTexcoords2); - DEBUG_EXTRA("生成纹理图集: 尺寸=%dx%d, UV范围=[%.3f,%.3f]-[%.3f,%.3f]", - textureSize, textureSize, - uvBounds.ptMin.x(), uvBounds.ptMin.y(), - uvBounds.ptMax.x(), uvBounds.ptMax.y()); + // 8. 打包纹理块 + std::vector generatedTextures; - // 添加调试信息 - DEBUG_EXTRA("colEmpty: R=%d, G=%d, B=%d", colEmpty.r, colEmpty.g, colEmpty.b); - DEBUG_EXTRA("OpenCV图像通道顺序: BGR"); + // 修正:使用正确的函数名和参数 + // 在 OpenMVS 中,纹理打包通常使用 PackTextureAtlases + PackTextureAtlases(faceTexcoords2, faceTexindices2, generatedTextures, + nTextureSizeMultiple, nRectPackingHeuristic, + colEmpty, maxTextureSize); - // 检查颜色通道顺序 - if (!sourceTextures.empty()) { - const Image8U3& firstTex = sourceTextures[0]; - cv::Vec3b firstPixel = firstTex.at(0, 0); - DEBUG_EXTRA("源纹理第一个像素: B=%d, G=%d, R=%d", - firstPixel[0], firstPixel[1], firstPixel[2]); + // 9. 高质量纹理采样 + GenerateHighQualityTexture(generatedTextures, faceTexcoords2, faceTexindices2, + fSharpnessWeight, colEmpty); + + // 10. 应用纹理锐化 + if (fSharpnessWeight > 0) { + ApplyAdaptiveSharpening(generatedTextures, fSharpnessWeight); } - // 3. 统计信息 - int processedFaces = 0; - int sampledPixels = 0; - int failedFaces = 0; + // 11. 填充空洞 + FillTextureHoles(generatedTextures, colEmpty); - // 4. 为每个面采样 - #ifdef _USE_OPENMP - #pragma omp parallel for schedule(dynamic) reduction(+:processedFaces, sampledPixels, failedFaces) - #endif - for (int_t idxFace = 0; idxFace < (int_t)scene.mesh.faces.size(); ++idxFace) { - const FIndex faceID = (FIndex)idxFace; - const Label label = faceLabels[faceID]; - - if (label == 0) { - failedFaces++; - continue; - } + // 12. 保存结果 + scene.mesh.texturesDiffuse = std::move(generatedTextures); + scene.mesh.faceTexindices = std::move(faceTexindices2); + + DEBUG_EXTRA("Texture generation with view consistency completed in %s", + TD_TIMER_GET_FMT().c_str()); + return true; +} + +// 视图一致性选择函数 +void MeshTexture::SelectOptimalViewsWithConsistency( + std::vector& faceViewData, + int minPatchSize) +{ + DEBUG_EXTRA("Selecting optimal views with consistency"); + + const int numFaces = (int)faces.size(); + const float consistencyWeight = 0.3f; // 一致性权重 + + // 步骤1: 计算每个面的候选视图质量 + std::vector>> faceCandidates(numFaces); + + #pragma omp parallel for schedule(dynamic) + for (int fid = 0; fid < numFaces; ++fid) { + const FIndex idxFace = (FIndex)fid; + const Face& face = faces[idxFace]; - const IIndex idxView = label - 1; - if (idxView >= images.size()) { - failedFaces++; - continue; + // 计算候选视图 + std::vector> candidates; + for (int viewID = 0; viewID < (int)images.size(); ++viewID) { + if (IsFaceVisibleFromView(idxFace, viewID)) { + float quality = ComputeViewQuality(idxFace, viewID); + candidates.emplace_back(viewID, quality); + } } - // 获取面的几何信息 - const TexCoord* meshUVs = &scene.mesh.faceTexcoords[faceID * 3]; - const Face& face = scene.mesh.faces[faceID]; - - // 获取源纹理信息 - const TexCoord* srcUVs = &sourceTexcoords[faceID * 3]; - const TexIndex textureIdx = sourceTexindices.empty() ? 0 : sourceTexindices[faceID]; + // 按质量排序 + std::sort(candidates.begin(), candidates.end(), + [](const auto& a, const auto& b) { return a.second > b.second; }); - if (textureIdx >= sourceTextures.size()) { - failedFaces++; - continue; + faceCandidates[fid] = std::move(candidates); + } + + // 步骤2: 迭代优化视图选择,考虑相邻面一致性 + std::vector selectedViews(numFaces, -1); + std::vector viewScores(numFaces, 0.0f); + + // 初始化:选择质量最高的视图 + #pragma omp parallel for schedule(static) + for (int fid = 0; fid < numFaces; ++fid) { + if (!faceCandidates[fid].empty()) { + selectedViews[fid] = faceCandidates[fid][0].first; + viewScores[fid] = faceCandidates[fid][0].second; } - - const Image8U3& sourceTexture = sourceTextures[textureIdx]; - - // 计算面的UV边界 - AABB2f faceBounds(true); + } + + // 构建顶点到面的映射,用于快速查找相邻面 + std::vector> vertexToFaces(vertices.size()); + for (FIndex fid = 0; fid < (FIndex)faces.size(); ++fid) { + const Face& face = faces[fid]; for (int i = 0; i < 3; ++i) { - faceBounds.InsertFull(meshUVs[i]); - } - - // 转换为像素坐标 - int startX = (int)(faceBounds.ptMin.x() * textureSize); - int startY = (int)(faceBounds.ptMin.y() * textureSize); - int endX = (int)(faceBounds.ptMax.x() * textureSize); - int endY = (int)(faceBounds.ptMax.y() * textureSize); - - // 边界检查 - startX = std::max(0, std::min(startX, textureSize - 1)); - startY = std::max(0, std::min(startY, textureSize - 1)); - endX = std::max(0, std::min(endX, textureSize - 1)); - endY = std::max(0, std::min(endY, textureSize - 1)); - - if (startX >= endX || startY >= endY) { - failedFaces++; - continue; + vertexToFaces[face[i]].push_back(fid); } + } + + // 迭代优化 + for (int iteration = 0; iteration < 5; ++iteration) { + int changes = 0; - int faceSampledPixels = 0; - - // 采样纹理 - for (int y = startY; y <= endY; ++y) { - for (int x = startX; x <= endX; ++x) { - const Point2f texCoord((x + 0.5f) / textureSize, (y + 0.5f) / textureSize); - - // 计算重心坐标 + #pragma omp parallel for schedule(dynamic) reduction(+:changes) + for (int fid = 0; fid < numFaces; ++fid) { + if (faceCandidates[fid].empty()) continue; + + const Face& face = faces[fid]; + std::vector neighborViews; + + // 通过共享顶点收集相邻面的视图 + std::unordered_set processedNeighbors; + + for (int i = 0; i < 3; ++i) { + const VIndex vertexIdx = face[i]; + const std::vector& adjacentFaces = vertexToFaces[vertexIdx]; + + for (FIndex neighborFid : adjacentFaces) { + // 跳过自己 + if (neighborFid == fid) continue; + + // 确保是真正的相邻面(共享边) + const Face& neighborFace = faces[neighborFid]; + bool sharesEdge = false; + + // 检查两个面是否共享一条边 + for (int j = 0; j < 3 && !sharesEdge; ++j) { + VIndex v1 = neighborFace[j]; + VIndex v2 = neighborFace[(j + 1) % 3]; + + // 检查边 (v1, v2) 是否在当前面中 + for (int k = 0; k < 3; ++k) { + VIndex w1 = face[k]; + VIndex w2 = face[(k + 1) % 3]; + + if ((v1 == w1 && v2 == w2) || (v1 == w2 && v2 == w1)) { + sharesEdge = true; + break; + } + } + } + + if (sharesEdge && selectedViews[neighborFid] != -1) { + if (processedNeighbors.find(neighborFid) == processedNeighbors.end()) { + neighborViews.push_back(selectedViews[neighborFid]); + processedNeighbors.insert(neighborFid); + } + } + } + } + + if (neighborViews.empty()) continue; + + // 计算相邻面中最常见的视图 + std::unordered_map viewCounts; + for (int viewID : neighborViews) { + viewCounts[viewID]++; + } + + int mostCommonView = -1; + int maxCount = 0; + for (const auto& pair : viewCounts) { + if (pair.second > maxCount) { + maxCount = pair.second; + mostCommonView = pair.first; + } + } + + // 重新计算视图得分,考虑一致性 + int bestView = selectedViews[fid]; + float bestScore = viewScores[fid]; + + for (const auto& candidate : faceCandidates[fid]) { + int viewID = candidate.first; + float quality = candidate.second; + + // 计算一致性得分 + float consistency = 0.0f; + if (viewID == mostCommonView) { + consistency = 0.5f * (static_cast(maxCount) / neighborViews.size()); + } + + // 综合得分 + float totalScore = (1.0f - consistencyWeight) * quality + + consistencyWeight * consistency; + + if (totalScore > bestScore) { + bestScore = totalScore; + bestView = viewID; + } + } + + if (bestView != selectedViews[fid]) { + selectedViews[fid] = bestView; + viewScores[fid] = bestScore; + changes++; + } + } + + DEBUG_EXTRA("View selection iteration %d: %d changes", iteration + 1, changes); + if (changes == 0) break; + } + + // 步骤3: 确保每个视图块至少有minPatchSize个面 + std::unordered_map> viewToFaces; + for (FIndex fid = 0; fid < (FIndex)selectedViews.size(); ++fid) { + int viewID = selectedViews[fid]; + if (viewID != -1) { + viewToFaces[viewID].push_back(fid); + } + } + + // 移除过小的视图块 + for (const auto& pair : viewToFaces) { + if ((int)pair.second.size() < minPatchSize) { + // 重新分配这些面 + for (FIndex fid : pair.second) { + // 寻找相邻面中最常见的视图 + std::unordered_map neighborViewCounts; + const Face& face = faces[fid]; + + for (int i = 0; i < 3; ++i) { + const VIndex vertexIdx = face[i]; + const std::vector& adjacentFaces = vertexToFaces[vertexIdx]; + + for (FIndex neighborFid : adjacentFaces) { + if (neighborFid != fid && selectedViews[neighborFid] != -1) { + neighborViewCounts[selectedViews[neighborFid]]++; + } + } + } + + // 选择最常见的相邻视图 + int bestNeighborView = -1; + int maxNeighborCount = 0; + for (const auto& countPair : neighborViewCounts) { + if (countPair.second > maxNeighborCount) { + maxNeighborCount = countPair.second; + bestNeighborView = countPair.first; + } + } + + if (bestNeighborView != -1) { + selectedViews[fid] = bestNeighborView; + } else { + // 如果没有合适的相邻视图,选择质量最高的候选视图 + if (!faceCandidates[fid].empty()) { + selectedViews[fid] = faceCandidates[fid][0].first; + } + } + } + } + } + + // 保存结果 + #pragma omp parallel for schedule(static) + for (int fid = 0; fid < numFaces; ++fid) { + faceViewData[fid].viewID = selectedViews[fid]; + faceViewData[fid].quality = viewScores[fid]; + } + + DEBUG_EXTRA("View selection with consistency completed"); +} + +// 创建一致性纹理块 +void MeshTexture::CreateConsistentTexturePatches( + const std::vector& faceViewData, + std::vector& patchAssignments, + int minPatchSize) +{ + DEBUG_EXTRA("Creating consistent texture patches"); + + const int numFaces = faces.size(); + std::vector visited(numFaces, false); + int patchCounter = 0; + + // 步骤1: 创建基于视图的纹理块 + std::vector> patchFaces; + + for (int startFace = 0; startFace < numFaces; ++startFace) { + if (visited[startFace] || faceViewData[startFace].viewID == -1) { + continue; + } + + int viewID = faceViewData[startFace].viewID; + std::vector currentPatch; + std::queue faceQueue; + + faceQueue.push(startFace); + visited[startFace] = true; + + while (!faceQueue.empty()) { + int fid = faceQueue.front(); + faceQueue.pop(); + + currentPatch.push_back(fid); + patchAssignments[fid] = patchCounter; + + const Face& face = faces[fid]; + + // 查找相同视图的相邻面 + for (int i = 0; i < 3; ++i) { + const VIndex idxV0 = face[i]; + const VIndex idxV1 = face[(i + 1) % 3]; + const Edge edge = mesh.EdgeFromVertices(idxV0, idxV1); + + if (edge != NO_ID) { + const FaceIdxArr& facePairs = mesh.edgeFaces[edge]; + for (FIndex neighborFid : facePairs) { + if (!visited[neighborFid] && + faceViewData[neighborFid].viewID == viewID) { + visited[neighborFid] = true; + faceQueue.push(neighborFid); + } + } + } + } + } + + // 只保留足够大的纹理块 + if (currentPatch.size() >= minPatchSize) { + patchFaces.push_back(std::move(currentPatch)); + patchCounter++; + } else { + // 小纹理块重新标记为未分配 + for (FIndex fid : currentPatch) { + patchAssignments[fid] = -1; + } + } + } + + // 步骤2: 处理未分配的面(分配到最近的纹理块) + std::vector unassignedFaces; + for (int fid = 0; fid < numFaces; ++fid) { + if (patchAssignments[fid] == -1 && faceViewData[fid].viewID != -1) { + unassignedFaces.push_back(fid); + } + } + + #pragma omp parallel for schedule(dynamic) + for (int idx = 0; idx < unassignedFaces.size(); ++idx) { + int fid = unassignedFaces[idx]; + int bestPatch = -1; + float bestDistance = FLT_MAX; + + for (int patchID = 0; patchID < patchFaces.size(); ++patchID) { + if (patchFaces[patchID].empty()) continue; + + // 查找最近的已分配面 + for (FIndex assignedFid : patchFaces[patchID]) { + float distance = ComputeFaceDistance(fid, assignedFid); + if (distance < bestDistance) { + bestDistance = distance; + bestPatch = patchID; + } + } + } + + if (bestPatch != -1) { + patchAssignments[fid] = bestPatch; + patchFaces[bestPatch].push_back(fid); + } + } + + // 步骤3: 创建纹理块 + texturePatches.Resize(patchFaces.size() + 1); // +1 用于无效面 + + #pragma omp parallel for schedule(static) + for (int patchID = 0; patchID < patchFaces.size(); ++patchID) { + TexturePatch& patch = texturePatches[patchID]; + patch.faces = patchFaces[patchID]; + + // 确定纹理块的视图(取最常见的视图) + std::unordered_map viewCounts; + for (FIndex fid : patch.faces) { + int viewID = faceViewData[fid].viewID; + if (viewID != -1) { + viewCounts[viewID]++; + } + } + + int dominantView = -1; + int maxCount = 0; + for (const auto& pair : viewCounts) { + if (pair.second > maxCount) { + maxCount = pair.second; + dominantView = pair.first; + } + } + + patch.label = dominantView; + } + + // 步骤4: 创建无效面纹理块 + TexturePatch& invalidPatch = texturePatches.back(); + invalidPatch.label = NO_ID; + for (int fid = 0; fid < numFaces; ++fid) { + if (faceViewData[fid].viewID == -1) { + invalidPatch.faces.Insert(fid); + } + } +} + +// 高质量纹理生成 +void MeshTexture::GenerateHighQualityTexture( + std::vector& textures, + const Mesh::TexCoordArr& faceTexcoords, + const Mesh::TexIndexArr& faceTexindices, + float fSharpnessWeight, + Pixel8U colEmpty) +{ + DEBUG_EXTRA("Generating high-quality texture with view consistency"); + + for (size_t texID = 0; texID < textures.size(); ++texID) { + Image8U3& texture = textures[texID]; + int texWidth = texture.cols; + int texHeight = texture.rows; + + // 为每个像素跟踪采样信息 + cv::Mat1f weightAccum(texHeight, texWidth, 0.0f); + cv::Mat1i viewAccum(texHeight, texWidth, -1); + cv::Mat3f colorAccum(texHeight, texWidth, cv::Vec3f(0, 0, 0)); + + // 第一次遍历:从最优视图采样 + #ifdef TEXOPT_USE_OPENMP + #pragma omp parallel for schedule(dynamic) + #endif + for (int_t idx = 0; idx < (int_t)texturePatches.size() - 1; ++idx) { + const TexturePatch& texturePatch = texturePatches[idx]; + if (texturePatch.label == NO_ID) continue; + + const Image& imageData = images[texturePatch.label]; + const cv::Mat& sourceImage = imageData.image; + + for (FIndex idxFace : texturePatch.faces) { + if (faceTexindices[idxFace] != texID) continue; + + const TexCoord* texcoords = &faceTexcoords[idxFace * 3]; + const Face& face = faces[idxFace]; + + // 计算纹理三角形边界 + AABB2f uvBounds(true); + for (int i = 0; i < 3; ++i) { + uvBounds.InsertFull(texcoords[i]); + } + + int startX = std::max(0, (int)floor(uvBounds.ptMin.x())); + int startY = std::max(0, (int)floor(uvBounds.ptMin.y())); + int endX = std::min(texWidth - 1, (int)ceil(uvBounds.ptMax.x())); + int endY = std::min(texHeight - 1, (int)ceil(uvBounds.ptMax.y())); + + // 遍历纹理三角形内的所有像素 + for (int y = startY; y <= endY; ++y) { + for (int x = startX; x <= endX; ++x) { + Point2f texCoord(x + 0.5f, y + 0.5f); + + // 检查像素是否在三角形内 + Point3f barycentric; + if (!PointInTriangle(texCoord, texcoords[0], + texcoords[1], texcoords[2], barycentric)) { + continue; + } + + // 计算3D坐标 + Vertex worldPoint = + vertices[face[0]] * barycentric.x + + vertices[face[1]] * barycentric.y + + vertices[face[2]] * barycentric.z; + + // 投影到源图像 + Point2f imgPoint = imageData.camera.ProjectPointP(worldPoint); + + if (imgPoint.x < 0 || imgPoint.x >= sourceImage.cols || + imgPoint.y < 0 || imgPoint.y >= sourceImage.rows) { + continue; + } + + // 高质量采样(双线性插值 + 各向异性过滤) + cv::Vec3f color = SampleHighQuality(sourceImage, imgPoint); + + #ifdef TEXOPT_USE_OPENMP + #pragma omp critical + #endif + { + // 累积颜色和权重 + colorAccum(y, x) += cv::Vec3f(color[2], color[1], color[0]); // BGR -> RGB + weightAccum(y, x) += 1.0f; + viewAccum(y, x) = texturePatch.label; // 记录主视图 + } + } + } + } + } + + // 第二次遍历:填充缺失像素,尽量从相同视图采样 + FillMissingPixelsWithViewConsistency(texture, colorAccum, weightAccum, + viewAccum, colEmpty); + } +} + +// 高质量采样函数 +cv::Vec3f MeshTexture::SampleHighQuality(const cv::Mat& image, const Point2f& point) +{ + int x0 = (int)floor(point.x); + int y0 = (int)floor(point.y); + int x1 = std::min(x0 + 1, image.cols - 1); + int y1 = std::min(y0 + 1, image.rows - 1); + + float fx = point.x - x0; + float fy = point.y - y0; + float fx1 = 1.0f - fx; + float fy1 = 1.0f - fy; + + // 双线性插值 + const cv::Vec3b& c00 = image.at(y0, x0); + const cv::Vec3b& c01 = image.at(y0, x1); + const cv::Vec3b& c10 = image.at(y1, x0); + const cv::Vec3b& c11 = image.at(y1, x1); + + cv::Vec3f result = + cv::Vec3f(c00[0], c00[1], c00[2]) * (fx1 * fy1) + + cv::Vec3f(c01[0], c01[1], c01[2]) * (fx * fy1) + + cv::Vec3f(c10[0], c10[1], c10[2]) * (fx1 * fy) + + cv::Vec3f(c11[0], c11[1], c11[2]) * (fx * fy); + + return result; +} + +// 基于视图一致性填充缺失像素 +void MeshTexture::FillMissingPixelsWithViewConsistency( + Image8U3& texture, + cv::Mat3f& colorAccum, + cv::Mat1f& weightAccum, + cv::Mat1i& viewAccum, + Pixel8U colEmpty) +{ + int rows = texture.rows; + int cols = texture.cols; + + // 首先,从有数据的像素直接赋值 + #pragma omp parallel for schedule(static) + for (int y = 0; y < rows; ++y) { + for (int x = 0; x < cols; ++x) { + float weight = weightAccum(y, x); + if (weight > 0) { + cv::Vec3f color = colorAccum(y, x) / weight; + texture.at(y, x) = cv::Vec3b( + (unsigned char)std::min(255.0f, std::max(0.0f, color[2])), // B + (unsigned char)std::min(255.0f, std::max(0.0f, color[1])), // G + (unsigned char)std::min(255.0f, std::max(0.0f, color[0])) // R + ); + } + } + } + + // 填充缺失的像素 + cv::Mat1b mask(rows, cols, (unsigned char)0); + for (int y = 0; y < rows; ++y) { + for (int x = 0; x < cols; ++x) { + if (weightAccum(y, x) == 0) { + mask(y, x) = 255; + } + } + } + + // 使用视图一致性进行填充 + int iterations = 0; + int remainingPixels = cv::countNonZero(mask); + + while (remainingPixels > 0 && iterations < 10) { + int filled = 0; + + #pragma omp parallel for schedule(dynamic) reduction(+:filled) + for (int y = 0; y < rows; ++y) { + for (int x = 0; x < cols; ++x) { + if (mask(y, x) == 0) continue; + + int dominantView = -1; + cv::Vec3f avgColor(0, 0, 0); + int count = 0; + + // 检查3x3邻域 + for (int dy = -1; dy <= 1; ++dy) { + for (int dx = -1; dx <= 1; ++dx) { + int nx = x + dx; + int ny = y + dy; + + if (nx >= 0 && nx < cols && ny >= 0 && ny < rows) { + if (weightAccum(ny, nx) > 0) { + int neighborView = viewAccum(ny, nx); + if (dominantView == -1 || neighborView == dominantView) { + dominantView = neighborView; + avgColor += cv::Vec3f( + texture.at(ny, nx)[2], + texture.at(ny, nx)[1], + texture.at(ny, nx)[0] + ); + count++; + } + } + } + } + } + + if (count > 0) { + cv::Vec3f finalColor = avgColor / count; + texture.at(y, x) = cv::Vec3b( + (unsigned char)finalColor[2], + (unsigned char)finalColor[1], + (unsigned char)finalColor[0] + ); + weightAccum(y, x) = 1.0f; + viewAccum(y, x) = dominantView; + mask(y, x) = 0; + filled++; + } + } + } + + remainingPixels -= filled; + iterations++; + + if (filled == 0) { + // 如果无法填充,使用更宽松的条件 + break; + } + } + + // 填充最后剩余的像素 + if (remainingPixels > 0) { + cv::inpaint(texture, mask, texture, 3, cv::INPAINT_TELEA); + } + + DEBUG_EXTRA("Filled %d missing pixels in %d iterations", + cv::countNonZero(weightAccum == 0) - remainingPixels, iterations); +} +float MeshTexture::ComputeViewQuality(FIndex idxFace, int viewID) +{ + const Face& face = faces[idxFace]; + const Image& imageData = images[viewID]; + + // 计算投影面积 + TexCoord texCoords[3]; + for (int i = 0; i < 3; ++i) { + texCoords[i] = imageData.camera.ProjectPointP(vertices[face[i]]); + } + + // 计算三角形面积 + float area = std::abs( + (texCoords[1].x - texCoords[0].x) * (texCoords[2].y - texCoords[0].y) - + (texCoords[1].y - texCoords[0].y) * (texCoords[2].x - texCoords[0].x) + ) * 0.5f; + + // 计算视角质量(法线夹角) + const Point3f& faceNormal = scene.mesh.faceNormals[idxFace]; + + // 计算面中心 + const cv::Point3f faceCenter = (vertices[face[0]] + vertices[face[1]] + vertices[face[2]]) / 3.0f; + + // 计算相机中心到面中心的方向 + // 注意:imageData.camera.C 是 CMatrix 类型,需要转换为 cv::Point3f + const cv::Point3f cameraCenter( + static_cast(imageData.camera.C.x), + static_cast(imageData.camera.C.y), + static_cast(imageData.camera.C.z) + ); + + const cv::Point3f viewDir = cameraCenter - faceCenter; + + // 归一化 + float viewLen = std::sqrt(viewDir.x * viewDir.x + + viewDir.y * viewDir.y + + viewDir.z * viewDir.z); + + if (viewLen > 0) { + cv::Point3f normalizedViewDir = viewDir * (1.0f / viewLen); + + // 计算点积 + float cosAngle = std::abs( + faceNormal.x * normalizedViewDir.x + + faceNormal.y * normalizedViewDir.y + + faceNormal.z * normalizedViewDir.z + ); + + // 计算图像分辨率 + float resolution = 1.0f; + + // 综合质量评分 + float quality = area * cosAngle * resolution; + + return quality; + } + + return 0.0f; +} + +float MeshTexture::ComputeFaceDistance(FIndex fid1, FIndex fid2) +{ + const Point3f center1 = (vertices[faces[fid1][0]] + vertices[faces[fid1][1]] + vertices[faces[fid1][2]]) / 3.0f; + const Point3f center2 = (vertices[faces[fid2][0]] + vertices[faces[fid2][1]] + vertices[faces[fid2][2]]) / 3.0f; + + Point3f diff = center2 - center1; + return std::sqrt(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z); +} + +// 判断面是否在视图中可见 +bool MeshTexture::IsFaceVisibleFromView(FIndex idxFace, int viewID) +{ + if (viewID < 0 || viewID >= (int)images.size()) { + return false; + } + + const Face& face = faces[idxFace]; + const Image& imageData = images[viewID]; + + // 检查面的三个顶点是否都在相机视锥体内 + for (int i = 0; i < 3; ++i) { + const cv::Point3f& vertex = vertices[face[i]]; + + // 投影到图像平面 + Point2f proj = imageData.camera.ProjectPointP(vertex); + + // 检查是否在图像范围内(添加边界容差) + const float border = 5.0f; + if (proj.x < -border || proj.x >= imageData.image.cols + border || + proj.y < -border || proj.y >= imageData.image.rows + border) { + return false; + } + + // 检查深度(如果需要) + // 这里可以添加深度测试逻辑 + } + + // 检查面法线和视图方向的夹角 + const Point3f& faceNormal = scene.mesh.faceNormals[idxFace]; + + // 计算面中心 + const cv::Point3f faceCenter = (vertices[face[0]] + vertices[face[1]] + vertices[face[2]]) * (1.0f / 3.0f); + + // 计算相机中心 + const cv::Point3f cameraCenter( + static_cast(imageData.camera.C.x), + static_cast(imageData.camera.C.y), + static_cast(imageData.camera.C.z) + ); + + // 计算视图方向 + cv::Point3f viewDir = cameraCenter - faceCenter; + float viewLen = cv::norm(viewDir); + + if (viewLen > 0) { + // 归一化 + viewDir /= viewLen; + + // 计算法线点积 + float cosAngle = faceNormal.x * viewDir.x + + faceNormal.y * viewDir.y + + faceNormal.z * viewDir.z; + + // 如果夹角太大(接近90度),面可能不可见 + // 使用阈值cos(85°) ≈ 0.087 + if (cosAngle < 0.1f) { + return false; + } + } + + return true; +} + + +// 优化纹理接缝 +void MeshTexture::OptimizeTextureSeams(const std::vector& textures, + const std::vector>& seamPoints) +{ + DEBUG_EXTRA("Optimizing texture seams"); + + if (textures.empty() || seamPoints.empty()) { + return; + } + + // 对每个纹理,在其边界处进行羽化 + for (size_t i = 0; i < textures.size(); ++i) { + cv::Mat texture = textures[i].clone(); + const std::vector& seams = seamPoints[i]; + + if (seams.empty()) { + continue; + } + + // 创建接缝掩模 + cv::Mat seamMask(texture.size(), CV_8UC1, cv::Scalar(0)); + + for (const Point2f& point : seams) { + int x = static_cast(point.x); + int y = static_cast(point.y); + + if (x >= 0 && x < texture.cols && y >= 0 && y < texture.rows) { + // 在接缝点周围创建羽化区域 + int radius = 3; // 羽化半径 + for (int dy = -radius; dy <= radius; ++dy) { + for (int dx = -radius; dx <= radius; ++dx) { + int nx = x + dx; + int ny = y + dy; + + if (nx >= 0 && nx < texture.cols && ny >= 0 && ny < texture.rows) { + float distance = std::sqrt(dx*dx + dy*dy); + float weight = std::max(0.0f, 1.0f - distance / radius); + uchar current = seamMask.at(ny, nx); + uchar newValue = cv::saturate_cast(current + weight * 255); + seamMask.at(ny, nx) = newValue; + } + } + } + } + } + + // 对掩模进行高斯模糊,创建平滑的过渡 + cv::GaussianBlur(seamMask, seamMask, cv::Size(5, 5), 0); + + // 应用羽化 + for (int y = 0; y < texture.rows; ++y) { + for (int x = 0; x < texture.cols; ++x) { + uchar maskValue = seamMask.at(y, x); + if (maskValue > 0) { + float alpha = maskValue / 255.0f; + + // 与相邻纹理混合 + // 这里可以添加与相邻纹理的混合逻辑 + // 暂时简单地进行高斯模糊 + cv::Vec3b& pixel = texture.at(y, x); + + // 计算3x3邻域的平均值 + cv::Vec3f sum(0, 0, 0); + int count = 0; + + for (int dy = -1; dy <= 1; ++dy) { + for (int dx = -1; dx <= 1; ++dx) { + int nx = x + dx; + int ny = y + dy; + + if (nx >= 0 && nx < texture.cols && ny >= 0 && ny < texture.rows) { + sum += cv::Vec3f(texture.at(ny, nx)); + ++count; + } + } + } + + if (count > 0) { + cv::Vec3f average = sum / count; + cv::Vec3b blended = cv::Vec3b( + cv::saturate_cast(pixel[0] * (1 - alpha) + average[0] * alpha), + cv::saturate_cast(pixel[1] * (1 - alpha) + average[1] * alpha), + cv::saturate_cast(pixel[2] * (1 - alpha) + average[2] * alpha) + ); + texture.at(y, x) = blended; + } + } + } + } + + // 更新纹理 + const_cast(textures[i]) = texture; + } + + DEBUG_EXTRA("Seam optimization completed"); +} +void MeshTexture::LocalSeamLevelingEnhanced() +{ + DEBUG_EXTRA("Applying enhanced local seam leveling"); + + // 首先调用原有的局部接缝平滑 + LocalSeamLeveling(); + + if (texturePatches.IsEmpty() || images.empty()) { + return; + } + + // 收集接缝边的信息 + std::vector> seamEdges; + + // 遍历所有面,查找在不同纹理块之间的边 + for (FIndex fid1 = 0; fid1 < (FIndex)faces.size(); ++fid1) { + const Face& face1 = faces[fid1]; + + // 获取面 fid1 的纹理块索引 + int patch1 = -1; + for (int i = 0; i < (int)texturePatches.size(); ++i) { + if (texturePatches[i].faces.Find(fid1) != NO_ID) { + patch1 = i; + break; + } + } + + if (patch1 == -1) continue; + + for (int i = 0; i < 3; ++i) { + VIndex idxV0 = face1[i]; + VIndex idxV1 = face1[(i + 1) % 3]; + + // 通过顶点查找共享这两个顶点的面 + std::vector adjacentFaces; + for (FIndex fid2 = 0; fid2 < (FIndex)faces.size(); ++fid2) { + if (fid2 == fid1) continue; + + const Face& face2 = faces[fid2]; + bool sharesEdge = false; + + // 检查面 face2 是否共享边 (idxV0, idxV1) + for (int j = 0; j < 3; ++j) { + int v1 = face2[j]; + int v2 = face2[(j + 1) % 3]; + if ((v1 == idxV0 && v2 == idxV1) || (v1 == idxV1 && v2 == idxV0)) { + sharesEdge = true; + break; + } + } + + if (sharesEdge) { + adjacentFaces.push_back(fid2); + } + } + + for (FIndex fid2 : adjacentFaces) { + if (fid2 <= fid1) continue; + + // 获取面 fid2 的纹理块索引 + int patch2 = -1; + for (int i = 0; i < (int)texturePatches.size(); ++i) { + if (texturePatches[i].faces.Find(fid2) != NO_ID) { + patch2 = i; + break; + } + } + + if (patch2 != -1 && patch1 != patch2) { + seamEdges.emplace_back(fid1, fid2); + } + } + } + } + + DEBUG_EXTRA("Found %zu seam edges for enhanced local leveling", seamEdges.size()); + + if (seamEdges.empty()) { + DEBUG_EXTRA("No seam edges found, nothing to do"); + return; + } + + // 存储每个顶点的颜色 + std::vector vertexColors(vertices.size(), cv::Vec3f(0, 0, 0)); + std::vector vertexCounts(vertices.size(), 0); + + // 为每个面计算顶点颜色 + for (FIndex fid = 0; fid < (FIndex)faces.size(); ++fid) { + const Face& face = faces[fid]; + + // 获取面对应的纹理块 + int patchIdx = -1; + for (int i = 0; i < (int)texturePatches.size(); ++i) { + if (texturePatches[i].faces.Find(fid) != NO_ID) { + patchIdx = i; + break; + } + } + + if (patchIdx == -1) continue; + + const TexturePatch& patch = texturePatches[patchIdx]; + + // 获取面对应的视图 + int viewID = patch.label; + if (viewID < 0 || viewID >= (int)images.size()) continue; + + const Image& image = images[viewID]; + + // 为面的每个顶点采样颜色 + for (int i = 0; i < 3; ++i) { + VIndex vid = face[i]; + const cv::Point3f& vertex = vertices[vid]; + + // 方法1: 使用 OpenMVS 中的点类型 + // 在 OpenMVS 中,通常使用 Point3f 而不是 cv::Point3f + // 我们需要转换类型 + Point3f point(vertex.x, vertex.y, vertex.z); + + // 方法2: 使用 OpenMVS Camera 类的正确投影函数 + // 根据 OpenMVS 源码,通常有两种方式: + // 1. camera.ProjectPoint(point) - 用于投影 3D 点 + // 2. 或者通过相机矩阵手动计算 + + Point2f proj; + + // 尝试第一种方法: 使用 ProjectPoint + proj = image.camera.ProjectPoint(point); + + // 如果上面失败,尝试第二种方法: 手动计算投影 + // Point2f proj = ProjectPoint(point, image.camera.K, image.camera.R, image.camera.C); + + // 检查投影点是否在图像内 + if (proj.x >= 0 && proj.x < image.image.cols && + proj.y >= 0 && proj.y < image.image.rows) { + + // 采样颜色 + int x = (int)std::floor(proj.x); + int y = (int)std::floor(proj.y); + + if (x >= 0 && x < image.image.cols && y >= 0 && y < image.image.rows) { + cv::Vec3b color = image.image.at(y, x); + + // 累积颜色 + vertexColors[vid] += cv::Vec3f(color[0], color[1], color[2]); + vertexCounts[vid]++; + } + } + } + } + + // 计算平均颜色 + for (size_t i = 0; i < vertexColors.size(); ++i) { + if (vertexCounts[i] > 0) { + vertexColors[i] /= (float)vertexCounts[i]; + } + } + + // 对每个接缝边进行颜色平滑 + for (const auto& seam : seamEdges) { + FIndex fid1 = seam.first; + FIndex fid2 = seam.second; + + const Face& face1 = faces[fid1]; + const Face& face2 = faces[fid2]; + + // 找到共享的顶点 + std::vector sharedVertices; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + if (face1[i] == face2[j]) { + sharedVertices.push_back(face1[i]); + } + } + } + + if (sharedVertices.size() >= 2) { + // 获取两个面对应的纹理块 + int patch1 = -1, patch2 = -1; + for (int i = 0; i < (int)texturePatches.size(); ++i) { + if (texturePatches[i].faces.Find(fid1) != NO_ID) { + patch1 = i; + } + if (texturePatches[i].faces.Find(fid2) != NO_ID) { + patch2 = i; + } + } + + if (patch1 == -1 || patch2 == -1) continue; + + // 计算两个纹理块的颜色差异 + cv::Vec3f avgColor1(0, 0, 0); + cv::Vec3f avgColor2(0, 0, 0); + int count1 = 0, count2 = 0; + + // 计算第一个面的平均颜色 + for (int i = 0; i < 3; ++i) { + VIndex vid = face1[i]; + if (vertexCounts[vid] > 0) { + avgColor1 += vertexColors[vid]; + count1++; + } + } + + // 计算第二个面的平均颜色 + for (int i = 0; i < 3; ++i) { + VIndex vid = face2[i]; + if (vertexCounts[vid] > 0) { + avgColor2 += vertexColors[vid]; + count2++; + } + } + + if (count1 > 0 && count2 > 0) { + avgColor1 /= (float)count1; + avgColor2 /= (float)count2; + + // 计算颜色差异 + cv::Vec3f colorDiff = avgColor2 - avgColor1; + + // 对共享顶点进行颜色调整 + for (VIndex vid : sharedVertices) { + if (vertexCounts[vid] > 0) { + // 简单的线性插值 + vertexColors[vid] = vertexColors[vid] + colorDiff * 0.3f; + + // 确保颜色值在有效范围内 + for (int c = 0; c < 3; ++c) { + if (vertexColors[vid][c] < 0) vertexColors[vid][c] = 0; + if (vertexColors[vid][c] > 255) vertexColors[vid][c] = 255; + } + } + } + } + } + } + + DEBUG_EXTRA("Enhanced local seam leveling completed. Processed %zu seam edges.", seamEdges.size()); + + // 注意:这里我们只计算了平滑后的颜色 + // 要将这些颜色应用到纹理中,需要在纹理生成过程中使用这些颜色 + // 这通常需要在纹理图集生成后,对纹理像素进行调整 +} + +// 合并重叠的纹理块 +void MeshTexture::MergeOverlappingPatches(Mesh::TexCoordArr& faceTexcoords2) +{ + DEBUG_EXTRA("Merging overlapping texture patches"); + + if (texturePatches.IsEmpty()) { + return; + } + + // 按视图分组纹理块 + std::unordered_map> patchesByView; + for (int i = 0; i < (int)texturePatches.size(); ++i) { + if (texturePatches[i].label != NO_ID) { + patchesByView[texturePatches[i].label].push_back(i); + } + } + + // 对每个视图的纹理块进行合并 + for (auto& viewPatches : patchesByView) { + std::vector& patchIndices = viewPatches.second; + if (patchIndices.size() <= 1) { + continue; // 不需要合并 + } + + // 计算纹理块之间的重叠程度 + std::vector>> overlaps; + + for (size_t i = 0; i < patchIndices.size(); ++i) { + TexturePatch& patch1 = texturePatches[patchIndices[i]]; + if (patch1.rect.empty()) continue; + + for (size_t j = i + 1; j < patchIndices.size(); ++j) { + TexturePatch& patch2 = texturePatches[patchIndices[j]]; + if (patch2.rect.empty()) continue; + + // 计算两个纹理块的重叠区域 + cv::Rect intersection = patch1.rect & patch2.rect; + if (!intersection.empty()) { + float overlapArea = intersection.area(); + float minArea = std::min(patch1.rect.area(), patch2.rect.area()); + float overlapRatio = overlapArea / minArea; + + if (overlapRatio > 0.1f) { // 重叠超过10% + overlaps.emplace_back(overlapRatio, + std::make_pair(patchIndices[i], patchIndices[j])); + } + } + } + } + + // 按重叠程度排序 + std::sort(overlaps.begin(), overlaps.end(), + [](const auto& a, const auto& b) { return a.first > b.first; }); + + // 合并高度重叠的纹理块 + std::vector merged(texturePatches.size(), false); + + for (const auto& overlap : overlaps) { + int idx1 = overlap.second.first; + int idx2 = overlap.second.second; + + if (merged[idx1] || merged[idx2]) { + continue; // 已经合并过了 + } + + TexturePatch& patch1 = texturePatches[idx1]; + TexturePatch& patch2 = texturePatches[idx2]; + + // 合并两个纹理块 + // 计算合并后的边界框 + cv::Rect mergedRect = patch1.rect | patch2.rect; + + // 合并面列表 + FaceIdxArr mergedFaces = patch1.faces; + for (FIndex fid : patch2.faces) { + mergedFaces.Insert(fid); + } + + // 创建新的纹理块 + patch1.rect = mergedRect; + patch1.faces = mergedFaces; + + // 标记第二个纹理块为已合并 + patch2.label = NO_ID; + patch2.faces.Release(); + merged[idx2] = true; + + DEBUG_VERBOSE("Merged patches %d and %d (overlap: %.2f%%)", + idx1, idx2, overlap.first * 100.0f); + } + } + + // 移除空的纹理块 + TexturePatchIdxArr newIndices(texturePatches.size()); + int newIdx = 0; + for (int i = 0; i < (int)texturePatches.size(); ++i) { + if (texturePatches[i].label != NO_ID && !texturePatches[i].faces.IsEmpty()) { + texturePatches[newIdx] = texturePatches[i]; + newIndices[i] = newIdx; + ++newIdx; + } else { + newIndices[i] = NO_ID; + } + } + texturePatches.Resize(newIdx); + + // 更新面的纹理索引 + for (FIndex fid = 0; fid < (FIndex)faces.size(); ++fid) { + int oldIdx = faceTexidx[fid]; + if (oldIdx != NO_ID) { + faceTexidx[fid] = newIndices[oldIdx]; + } + } + + DEBUG_EXTRA("Merged overlapping patches, new count: %d", texturePatches.size()); +} + +// 打包纹理块 +void MeshTexture::PackTexturePatches(const Mesh::TexCoordArr& faceTexcoords2, + const Mesh::TexIndexArr& faceTexindices2, + std::vector& generatedTextures, + unsigned nTextureSizeMultiple, + unsigned nRectPackingHeuristic, + int maxTextureSize) +{ + DEBUG_EXTRA("Packing texture patches with enhanced algorithm"); + + if (texturePatches.IsEmpty()) { + DEBUG_EXTRA("No texture patches to pack"); + return; + } + + // 收集所有需要打包的纹理块 + std::vector validPatches; + for (int i = 0; i < (int)texturePatches.size(); ++i) { + if (texturePatches[i].label != NO_ID && !texturePatches[i].rect.empty()) { + validPatches.push_back(&texturePatches[i]); + } + } + + if (validPatches.empty()) { + DEBUG_EXTRA("No valid texture patches to pack"); + return; + } + + // 按视图分组纹理块 + std::unordered_map> patchesByView; + for (TexturePatch* patch : validPatches) { + patchesByView[patch->label].push_back(patch); + } + + // 为每个视图创建一个纹理图集 + generatedTextures.clear(); + std::vector> textureAtlases; + + for (auto& viewPatches : patchesByView) { + std::vector& patches = viewPatches.second; + + // 对纹理块按大小排序(从大到小) + std::sort(patches.begin(), patches.end(), + [](const TexturePatch* a, const TexturePatch* b) { + return a->rect.area() > b->rect.area(); + }); + + // 使用矩形打包算法 + std::vector rectangles; + std::vector rectPatches; + + for (TexturePatch* patch : patches) { + rectangles.push_back(patch->rect); + rectPatches.push_back(patch); + } + + // 计算所需纹理大小 + int totalArea = 0; + for (const cv::Rect& rect : rectangles) { + totalArea += rect.area(); + } + + // 估计纹理大小(考虑填充) + int estimatedSize = (int)std::ceil(std::sqrt(totalArea * 1.5f)); + + // 对齐到倍数 + if (nTextureSizeMultiple > 1) { + estimatedSize = ((estimatedSize + nTextureSizeMultiple - 1) / nTextureSizeMultiple) * nTextureSizeMultiple; + } + + // 限制最大大小 + if (maxTextureSize > 0 && estimatedSize > maxTextureSize) { + estimatedSize = maxTextureSize; + } + + // 创建纹理图集 + cv::Mat texture(estimatedSize, estimatedSize, CV_8UC3, cv::Scalar(0, 0, 0)); + + // 简单的打包策略:从左到右,从上到下 + int x = 0, y = 0; + int maxRowHeight = 0; + + for (size_t i = 0; i < rectangles.size(); ++i) { + cv::Rect& rect = rectangles[i]; + TexturePatch* patch = rectPatches[i]; + + // 检查是否适合当前行 + if (x + rect.width > estimatedSize) { + // 换行 + x = 0; + y += maxRowHeight; + maxRowHeight = 0; + } + + // 检查是否适合纹理 + if (y + rect.height > estimatedSize) { + // 纹理太小,需要调整 + DEBUG_EXTRA("Texture atlas too small, increasing size"); + // 这里可以增加纹理大小或使用更复杂的打包算法 + break; + } + + // 放置纹理块 + patch->rect.x = x; + patch->rect.y = y; + x += rect.width; + + if (rect.height > maxRowHeight) { + maxRowHeight = rect.height; + } + } + + generatedTextures.push_back(texture); + textureAtlases.push_back(patches); + } + + DEBUG_EXTRA("Packed %zu texture atlases", generatedTextures.size()); +} + +// 自适应锐化 +void MeshTexture::ApplyAdaptiveSharpening(std::vector& textures, float fSharpnessWeight) +{ + if (fSharpnessWeight <= 0.0f || textures.empty()) { + return; + } + + DEBUG_EXTRA("Applying adaptive sharpening to textures (weight: %.2f)", fSharpnessWeight); + + for (size_t i = 0; i < textures.size(); ++i) { + cv::Mat& texture = textures[i]; + if (texture.empty()) continue; + + // 计算图像的梯度幅度,用于自适应锐化 + cv::Mat gray, gradient; + cv::cvtColor(texture, gray, cv::COLOR_BGR2GRAY); + + // 计算Sobel梯度 + cv::Mat gradX, gradY; + cv::Sobel(gray, gradX, CV_32F, 1, 0, 3); + cv::Sobel(gray, gradY, CV_32F, 0, 1, 3); + cv::magnitude(gradX, gradY, gradient); + + // 归一化梯度 + double minVal, maxVal; + cv::minMaxLoc(gradient, &minVal, &maxVal); + if (maxVal > minVal) { + gradient = (gradient - minVal) / (maxVal - minVal); + } + + // 对图像进行锐化 + cv::Mat sharpened; + + // 使用非锐化掩模 + cv::Mat blurred; + cv::GaussianBlur(texture, blurred, cv::Size(0, 0), 3.0); + + // 自适应锐化:在边缘区域使用更强的锐化 + for (int y = 0; y < texture.rows; ++y) { + for (int x = 0; x < texture.cols; ++x) { + float edgeStrength = gradient.at(y, x); + float weight = fSharpnessWeight * (0.5f + edgeStrength * 0.5f); + + cv::Vec3b& srcPixel = texture.at(y, x); + cv::Vec3b& blurPixel = blurred.at(y, x); + cv::Vec3b& dstPixel = sharpened.at(y, x); + + for (int c = 0; c < 3; ++c) { + int value = static_cast(srcPixel[c] + weight * (srcPixel[c] - blurPixel[c])); + dstPixel[c] = cv::saturate_cast(value); + } + } + } + + texture = sharpened; + } + + DEBUG_EXTRA("Adaptive sharpening completed"); +} + +// 填充纹理空洞 +void MeshTexture::FillTextureHoles(std::vector& textures, Pixel8U colEmpty) +{ + if (textures.empty()) { + return; + } + + DEBUG_EXTRA("Filling holes in textures"); + + cv::Scalar emptyColor(colEmpty.r, colEmpty.g, colEmpty.b); + + for (size_t i = 0; i < textures.size(); ++i) { + cv::Mat& texture = textures[i]; + if (texture.empty()) continue; + + // 创建掩模,标识空洞区域 + cv::Mat mask(texture.size(), CV_8UC1, cv::Scalar(0)); + + for (int y = 0; y < texture.rows; ++y) { + for (int x = 0; x < texture.cols; ++x) { + cv::Vec3b pixel = texture.at(y, x); + if (pixel[0] == emptyColor[0] && + pixel[1] == emptyColor[1] && + pixel[2] == emptyColor[2]) { + mask.at(y, x) = 255; + } + } + } + + int holeCount = cv::countNonZero(mask); + if (holeCount == 0) { + continue; // 没有空洞 + } + + DEBUG_VERBOSE("Texture %zu: filling %d holes", i, holeCount); + + // 使用修复算法填充空洞 + cv::Mat inpainted; + cv::inpaint(texture, mask, inpainted, 3, cv::INPAINT_TELEA); + + // 将修复的区域复制回原图像 + for (int y = 0; y < texture.rows; ++y) { + for (int x = 0; x < texture.cols; ++x) { + if (mask.at(y, x) == 255) { + texture.at(y, x) = inpainted.at(y, x); + } + } + } + } + + DEBUG_EXTRA("Hole filling completed"); +} + +bool MeshTexture::TextureWithExistingUV( + const IIndexArr& views, + int nIgnoreMaskLabel, + float fOutlierThreshold, + unsigned nTextureSizeMultiple, + Pixel8U colEmpty, + float fSharpnessWeight, + const Mesh::Image8U3Arr& existingTextures, + const Mesh::TexCoordArr& existingTexcoords, + const Mesh::TexIndexArr& existingTexindices) +{ + DEBUG_EXTRA("TextureWithExistingUV - 使用3D几何坐标作为桥梁"); + TD_TIMER_START(); + + // 1. 验证输入 + if (scene.mesh.faceTexcoords.empty()) { + VERBOSE("error: mesh does not contain UV coordinates"); + return false; + } + + if (existingTextures.empty()) { + VERBOSE("error: no existing texture data provided"); + return false; + } + + DEBUG_EXTRA("Processing %zu faces with existing texture data", scene.mesh.faces.size()); + + // 2. 为每个面选择最佳视图(从原始图像,而不是已有纹理) + FaceDataViewArr facesDatas; + if (!ListCameraFaces(facesDatas, fOutlierThreshold, nIgnoreMaskLabel, views, false)) { + return false; + } + + // 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 = GenerateTextureAtlasWith3DBridge( + faceLabels, views, existingTextures, existingTexcoords, existingTexindices, + nTextureSizeMultiple, colEmpty, fSharpnessWeight + ); + + if (!generatedTextures.empty()) { + // 检查颜色通道 + CheckColorChannels(generatedTextures[0], "生成的纹理"); + + // 检查源纹理颜色通道 + if (!existingTextures.empty()) { + CheckColorChannels(existingTextures[0], "源纹理"); + } + + // 保存纹理 + scene.mesh.texturesDiffuse = std::move(generatedTextures); + + // 设置面的纹理索引 + if (scene.mesh.texturesDiffuse.size() > 1) { + scene.mesh.faceTexindices.resize(scene.mesh.faces.size()); + for (size_t i = 0; i < scene.mesh.faces.size(); ++i) { + scene.mesh.faceTexindices[i] = 0; + } + } else { + scene.mesh.faceTexindices.Release(); + } + + DEBUG_EXTRA("Generated %zu textures from existing data", + scene.mesh.texturesDiffuse.size()); + return true; + } + + DEBUG_EXTRA("Texture generation failed"); + return false; +} + +Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasWith3DBridge( + const LabelArr& faceLabels, + const IIndexArr& views, + const Mesh::Image8U3Arr& sourceTextures, + const Mesh::TexCoordArr& sourceTexcoords, + const Mesh::TexIndexArr& sourceTexindices, + unsigned nTextureSizeMultiple, + Pixel8U colEmpty, + float fSharpnessWeight) +{ + DEBUG_EXTRA("GenerateTextureAtlasWith3DBridge - 使用3D几何坐标作为桥梁"); + + // 1. 分析外部UV布局 + AABB2f uvBounds(true); + FOREACH(i, scene.mesh.faceTexcoords) { + const TexCoord& uv = scene.mesh.faceTexcoords[i]; + uvBounds.InsertFull(uv); + } + + // 确保UV在[0,1]范围内 + if (uvBounds.ptMin.x() < 0 || uvBounds.ptMin.y() < 0 || + uvBounds.ptMax.x() > 1 || uvBounds.ptMax.y() > 1) { + DEBUG_EXTRA("UV超出[0,1]范围,进行归一化"); + uvBounds = AABB2f(true); + for (size_t i = 0; i < scene.mesh.faceTexcoords.size(); i += 3) { + for (int v = 0; v < 3; ++v) { + const TexCoord& uv = scene.mesh.faceTexcoords[i + v]; + uvBounds.InsertFull(uv); + } + } + } + + // 计算纹理尺寸 + const float uvWidth = uvBounds.ptMax.x() - uvBounds.ptMin.x(); + const float uvHeight = uvBounds.ptMax.y() - uvBounds.ptMin.y(); + const int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple); + +// 创建目标纹理 + Mesh::Image8U3Arr textures; + Image8U3& textureAtlas = textures.emplace_back(textureSize, textureSize); + // 注意:cv::Scalar 使用 BGR 顺序 + textureAtlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); + + DEBUG_EXTRA("生成纹理图集: 尺寸=%dx%d, UV范围=[%.3f,%.3f]-[%.3f,%.3f]", + textureSize, textureSize, + uvBounds.ptMin.x(), uvBounds.ptMin.y(), + uvBounds.ptMax.x(), uvBounds.ptMax.y()); + + // 添加调试信息 + DEBUG_EXTRA("colEmpty: R=%d, G=%d, B=%d", colEmpty.r, colEmpty.g, colEmpty.b); + DEBUG_EXTRA("OpenCV图像通道顺序: BGR"); + + // 检查颜色通道顺序 + if (!sourceTextures.empty()) { + const Image8U3& firstTex = sourceTextures[0]; + cv::Vec3b firstPixel = firstTex.at(0, 0); + DEBUG_EXTRA("源纹理第一个像素: B=%d, G=%d, R=%d", + firstPixel[0], firstPixel[1], firstPixel[2]); + } + + // 3. 统计信息 + int processedFaces = 0; + int sampledPixels = 0; + int failedFaces = 0; + + // 4. 为每个面采样 + #ifdef _USE_OPENMP + #pragma omp parallel for schedule(dynamic) reduction(+:processedFaces, sampledPixels, failedFaces) + #endif + for (int_t idxFace = 0; idxFace < (int_t)scene.mesh.faces.size(); ++idxFace) { + const FIndex faceID = (FIndex)idxFace; + const Label label = faceLabels[faceID]; + + if (label == 0) { + failedFaces++; + continue; + } + + const IIndex idxView = label - 1; + if (idxView >= images.size()) { + failedFaces++; + continue; + } + + // 获取面的几何信息 + const TexCoord* meshUVs = &scene.mesh.faceTexcoords[faceID * 3]; + const Face& face = scene.mesh.faces[faceID]; + + // 获取源纹理信息 + const TexCoord* srcUVs = &sourceTexcoords[faceID * 3]; + const TexIndex textureIdx = sourceTexindices.empty() ? 0 : sourceTexindices[faceID]; + + if (textureIdx >= sourceTextures.size()) { + failedFaces++; + continue; + } + + const Image8U3& sourceTexture = sourceTextures[textureIdx]; + + // 计算面的UV边界 + AABB2f faceBounds(true); + for (int i = 0; i < 3; ++i) { + faceBounds.InsertFull(meshUVs[i]); + } + + // 转换为像素坐标 + int startX = (int)(faceBounds.ptMin.x() * textureSize); + int startY = (int)(faceBounds.ptMin.y() * textureSize); + int endX = (int)(faceBounds.ptMax.x() * textureSize); + int endY = (int)(faceBounds.ptMax.y() * textureSize); + + // 边界检查 + startX = std::max(0, std::min(startX, textureSize - 1)); + startY = std::max(0, std::min(startY, textureSize - 1)); + endX = std::max(0, std::min(endX, textureSize - 1)); + endY = std::max(0, std::min(endY, textureSize - 1)); + + if (startX >= endX || startY >= endY) { + failedFaces++; + continue; + } + + int faceSampledPixels = 0; + + // 采样纹理 + for (int y = startY; y <= endY; ++y) { + for (int x = startX; x <= endX; ++x) { + const Point2f texCoord((x + 0.5f) / textureSize, (y + 0.5f) / textureSize); + + // 计算重心坐标 Point3f barycentric; if (!PointInTriangle(texCoord, meshUVs[0], meshUVs[1], meshUVs[2], barycentric)) { continue; @@ -11444,9 +13229,16 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi Mesh::TexCoordArr existingTexcoords; Mesh::TexIndexArr existingTexindices; - texture.GenerateTextureForUV(bGlobalSeamLeveling, bLocalSeamLeveling, nTextureSizeMultiple, - nRectPackingHeuristic, colEmpty, fSharpnessWeight, maxTextureSize, - baseFileName, bOriginFaceview, this, existingTexcoords, existingTexindices); + // texture.GenerateTextureForUV(bGlobalSeamLeveling, bLocalSeamLeveling, nTextureSizeMultiple, + // nRectPackingHeuristic, colEmpty, fSharpnessWeight, maxTextureSize, + // baseFileName, bOriginFaceview, this, existingTexcoords, existingTexindices); + + if (!texture.GenerateTextureWithViewConsistency( + bGlobalSeamLeveling, bLocalSeamLeveling, nTextureSizeMultiple, + nRectPackingHeuristic, colEmpty, fSharpnessWeight, maxTextureSize, + baseFileName, bOriginFaceview, this)) { + return false; + } // 保存生成的纹理数据 Mesh::Image8U3Arr existingTextures = texture.texturesDiffuseTemp;