From c70d56d3cbae42aaa9947e062c215771831ef896 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Mon, 4 May 2026 11:09:30 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=AD=E9=97=B4=E7=89=88=E6=9C=AC=EF=BC=8C?= =?UTF-8?q?=E9=80=9A=E8=BF=87=E7=BC=96=E8=AF=91=EF=BC=8C=E4=BD=86=E6=98=AF?= =?UTF-8?q?=E8=B4=B4=E5=9B=BE=E6=95=88=E6=9E=9C=E4=B8=8D=E5=A5=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 1560 ++++++++++++++++++++++++++++++++----- 1 file changed, 1384 insertions(+), 176 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index b74e7fa..4dc1c9d 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -192,14 +192,6 @@ struct PatchQualityInfo { std::vector faceQualities; }; - -// 在函数外部定义 PixelSample -struct PixelSample { - cv::Vec3f color; // 颜色(BGR格式) - float weight; // 权重 - IIndex viewIdx; // 视图索引 -}; - std::vector patchQualityInfos; struct MeshTexture { @@ -535,6 +527,67 @@ public: const Mesh::TexCoordArr& existingTexcoords, // 添加已有UV参数 const Mesh::TexIndexArr& existingTexindices // 添加已有纹理索引参数 ); + + struct PixelSample { + cv::Vec3f color; // BGR 颜色 + float weight; // 权重 + IIndex viewIdx; // 视图索引 + }; + + struct FaceViewScore { + IIndex viewIdx; // 视图索引 + float score; // 视图得分 + float quality; // 视图质量 + }; + + struct EnhancedPixelSample { + cv::Vec3f color; // BGR颜色 + float weight; // 权重 + IIndex viewIdx; // 视图索引 + float viewQuality; // 视图质量评分 + float resolution; // 分辨率 + float viewAngle; // 视角角度 + + EnhancedPixelSample(const cv::Vec3f& c, float w, IIndex v, float q, float r, float a) + : color(c), weight(w), viewIdx(v), viewQuality(q), resolution(r), viewAngle(a) {} + + // 计算样本的综合质量 + float GetOverallQuality() const { + return viewQuality * resolution * viewAngle; + } + }; + + // 在 MeshTexture 结构体声明中添加以下函数 + void BuildFaceNeighbors(std::vector>& faceNeighbors) const; + void OptimizeViewSelectionWithGraphCut(LabelArr& faceLabels, const std::vector>& faceNeighbors); + std::vector SelectHighQualityAuxiliaryViews(FIndex faceID, IIndex primaryView, const LabelArr& faceLabels, const IIndexArr& views); + void ProcessFaceForTextureSampling(FIndex faceID, IIndex viewIdx, const Mesh::TexCoordArr& texcoords, int textureSize, + std::vector>& pixelSamples, int& totalSamples, bool isPrimary); + Pixel8U FuseColorsIntelligently(const std::vector& samples, int pixelIdx, int textureSize, + const std::vector>& allPixelSamples); + void ApplyAdaptiveColorCorrection(Image8U3& texture, Pixel8U colEmpty, float strength); + void ApplyEdgePreservingSharpening(Image8U3& texture, float strength, Pixel8U colEmpty); + Mesh::Image8U3Arr GenerateTextureAtlasWithEnhancedConsistency( + const LabelArr& faceLabels, + const IIndexArr& views, + const Mesh::TexCoordArr& sourceTexcoords, + unsigned nTextureSizeMultiple, + Pixel8U colEmpty, + float fSharpnessWeight); + void ProcessFaceForTexture( + FIndex faceID, + IIndex viewIdx, + const Mesh::TexCoordArr& texcoords, + int textureSize, + std::vector>& pixelSamples, + int& sampleCounter, + bool isPrimaryView); + void ApplyBoundarySmoothing( + Image8U3& texture, + Pixel8U colEmpty, + int textureSize); + void ApplyBalancedColorCorrection(Image8U3& texture, Pixel8U colEmpty, float strength); + void ApplyConservativeSharpening(Image8U3& texture, float strength, Pixel8U colEmpty); bool GenerateTextureWithViewConsistency( bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, @@ -13651,25 +13704,27 @@ bool MeshTexture::TextureWithExistingUV( 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()); + // 注意:这里故意不检查 existingTextures 是否为空 + // 因为这个函数的核心是从原始图像重新采样,不是从现有纹理复制 + DEBUG_EXTRA("Processing %zu faces with existing UV data", scene.mesh.faces.size()); - // 2. 为每个面选择最佳视图(从原始图像,而不是已有纹理) + // 2. 重新为每个面选择最佳视图 FaceDataViewArr facesDatas; if (!ListCameraFaces(facesDatas, fOutlierThreshold, nIgnoreMaskLabel, views, false)) { return false; } - // 3. 为每个面分配最佳视图 + // 3. 为每个面分配最佳视图(使用改进的算法) LabelArr faceLabels(scene.mesh.faces.size()); - FOREACH(idxFace, scene.mesh.faces) { + + // 步骤1:计算相邻面关系 + std::vector> faceNeighbors(scene.mesh.faces.size()); + + // 为每个面选择最佳视图 + for (FIndex idxFace = 0; idxFace < scene.mesh.faces.size(); ++idxFace) { const FaceDataArr& faceDatas = facesDatas[idxFace]; if (faceDatas.empty()) { - faceLabels[idxFace] = 0; // 无视图可用 + faceLabels[idxFace] = 0; continue; } @@ -13677,7 +13732,7 @@ bool MeshTexture::TextureWithExistingUV( float bestQuality = -1; IIndex bestView = NO_ID; for (const FaceData& data : faceDatas) { - if (data.quality > bestQuality && !data.bInvalidFacesRelative) { + if (!data.bInvalidFacesRelative && data.quality > bestQuality) { bestQuality = data.quality; bestView = data.idxView; } @@ -13686,21 +13741,18 @@ bool MeshTexture::TextureWithExistingUV( faceLabels[idxFace] = (bestView != NO_ID) ? (bestView + 1) : 0; } - // 4. 生成纹理图集 - Mesh::Image8U3Arr generatedTextures = GenerateTextureAtlasWith3DBridge( - faceLabels, views, existingTextures, existingTexcoords, existingTexindices, + // 步骤2:使用图割优化视图选择 + BuildFaceNeighbors(faceNeighbors); + OptimizeViewSelectionWithGraphCut(faceLabels, faceNeighbors); + + // 4. 生成纹理图集(使用改进的版本) + Mesh::Image8U3Arr generatedTextures = GenerateTextureAtlasWithEnhancedConsistency( + // faceLabels, views, existingTexcoords, + faceLabels, views, scene.mesh.faceTexcoords, nTextureSizeMultiple, colEmpty, fSharpnessWeight ); if (!generatedTextures.empty()) { - // 检查颜色通道 - CheckColorChannels(generatedTextures[0], "生成的纹理"); - - // 检查源纹理颜色通道 - if (!existingTextures.empty()) { - CheckColorChannels(existingTextures[0], "源纹理"); - } - // 保存纹理 scene.mesh.texturesDiffuse = std::move(generatedTextures); @@ -13714,7 +13766,7 @@ bool MeshTexture::TextureWithExistingUV( scene.mesh.faceTexindices.Release(); } - DEBUG_EXTRA("Generated %zu textures from existing data", + DEBUG_EXTRA("Generated %zu textures from existing UV data", scene.mesh.texturesDiffuse.size()); return true; } @@ -13723,184 +13775,1340 @@ bool MeshTexture::TextureWithExistingUV( return false; } -void MeshTexture::FillTextureGaps(Image8U3& texture, Pixel8U colEmpty) { - if (texture.empty()) return; +void MeshTexture::ApplyAdaptiveColorCorrection(Image8U3& texture, Pixel8U colEmpty, float strength) { + if (strength <= 0) return; - cv::Mat textureMat = (cv::Mat&)texture; + cv::Mat& textureMat = (cv::Mat&)texture; + + // 统计非背景像素 + cv::Vec3d sum(0, 0, 0); + int count = 0; - // 创建掩码 - cv::Mat mask = cv::Mat::zeros(texture.rows, texture.cols, CV_8UC1); for (int y = 0; y < texture.rows; ++y) { for (int x = 0; x < texture.cols; ++x) { - if (texture(y, x) != colEmpty) { - mask.at(y, x) = 255; + Pixel8U pixel = texture(y, x); + if (pixel != colEmpty) { + sum[0] += pixel.r; // R + sum[1] += pixel.g; // G + sum[2] += pixel.b; // B + count++; } } } - // 使用图像修复算法填充空隙 - cv::Mat inpainted; - cv::inpaint(textureMat, mask, inpainted, 3, cv::INPAINT_NS); + if (count == 0) return; - // 只替换空区域 + cv::Vec3d mean = sum / count; + double avgGray = (mean[0] + mean[1] + mean[2]) / 3.0; + + // 计算增益因子 + double gainR = 1.0, gainG = 1.0, gainB = 1.0; + if (avgGray > 0) { + gainR = avgGray / mean[0]; + gainG = avgGray / mean[1]; + gainB = avgGray / mean[2]; + } + + // 限制调整幅度 + gainR = std::max(0.9, std::min(gainR, 1.1)); + gainG = std::max(0.9, std::min(gainG, 1.1)); + gainB = std::max(0.9, std::min(gainB, 1.1)); + + DEBUG_EXTRA("全局颜色校正: 平均颜色 R=%.1f, G=%.1f, B=%.1f", mean[0], mean[1], mean[2]); + DEBUG_EXTRA("全局颜色校正: 增益因子 R=%.3f, G=%.3f, B=%.3f", gainR, gainG, gainB); + + // 应用颜色校正 for (int y = 0; y < texture.rows; ++y) { for (int x = 0; x < texture.cols; ++x) { - if (texture(y, x) == colEmpty) { - cv::Vec3b color = inpainted.at(y, x); - Pixel8U pixel; - pixel.b = color[0]; - pixel.g = color[1]; - pixel.r = color[2]; - texture(y, x) = pixel; - } + Pixel8U& pixel = texture(y, x); + if (pixel == colEmpty) continue; + + // 线性混合原始颜色和校正颜色 + double r = pixel.r * (1.0 - strength) + pixel.r * gainR * strength; + double g = pixel.g * (1.0 - strength) + pixel.g * gainG * strength; + double b = pixel.b * (1.0 - strength) + pixel.b * gainB * strength; + + pixel.r = (unsigned char)cv::saturate_cast(r); + pixel.g = (unsigned char)cv::saturate_cast(g); + pixel.b = (unsigned char)cv::saturate_cast(b); } } } -void MeshTexture::ApplyColorConsistencyOptimization( - Image8U3& texture, - const std::vector>& pixelSamples, - int textureSize, - Pixel8U colEmpty) -{ - if (pixelSamples.empty()) return; - - cv::Mat textureMat = (cv::Mat&)texture; +void MeshTexture::ApplyEdgePreservingSharpening(Image8U3& texture, float strength, Pixel8U colEmpty) { + if (strength <= 0) return; - // 创建临时的颜色缓冲区 - cv::Mat3f newColor(texture.rows, texture.cols, cv::Vec3f(0, 0, 0)); - cv::Mat1f newWeight(texture.rows, texture.cols, 0.0f); + cv::Mat& textureMat = (cv::Mat&)texture; - // 计算邻域颜色一致性 - const int kernelSize = 3; - const int halfKernel = kernelSize / 2; + // 使用轻微的高斯模糊 + cv::Mat blurred; + cv::GaussianBlur(textureMat, blurred, cv::Size(3, 3), 0.5); - for (int y = halfKernel; y < texture.rows - halfKernel; ++y) { - for (int x = halfKernel; x < texture.cols - halfKernel; ++x) { - int pixelIdx = y * textureSize + x; - const auto& samples = pixelSamples[pixelIdx]; - - if (samples.empty()) continue; - - // 收集邻域颜色 - std::vector neighborColors; - for (int dy = -halfKernel; dy <= halfKernel; ++dy) { - for (int dx = -halfKernel; dx <= halfKernel; ++dx) { - int nx = x + dx; - int ny = y + dy; - int neighborIdx = ny * textureSize + nx; - - if (!pixelSamples[neighborIdx].empty()) { - // 获取当前像素颜色 - Pixel8U pixel = texture(ny, nx); - if (pixel != colEmpty) { - neighborColors.push_back(cv::Vec3f(pixel.b, pixel.g, pixel.r)); - } - } - } - } - - if (neighborColors.size() < 3) continue; // 需要足够的邻域信息 - - // 计算颜色统计 - cv::Vec3f mean(0, 0, 0); - for (const auto& color : neighborColors) { - mean += color; - } - mean /= (float)neighborColors.size(); - - // 计算方差 - cv::Vec3f variance(0, 0, 0); - for (const auto& color : neighborColors) { - cv::Vec3f diff = color - mean; - variance[0] += diff[0] * diff[0]; - variance[1] += diff[1] * diff[1]; - variance[2] += diff[2] * diff[2]; + // 计算细节层 + cv::Mat detail = textureMat.clone(); + for (int y = 0; y < texture.rows; ++y) { + for (int x = 0; x < texture.cols; ++x) { + Pixel8U pixel = texture(y, x); + if (pixel == colEmpty) { + detail.at(y, x) = cv::Vec3b(colEmpty.b, colEmpty.g, colEmpty.r); + continue; } - variance /= (float)neighborColors.size(); - // 获取当前像素颜色 - Pixel8U currentPixel = texture(y, x); - cv::Vec3f currentColor(currentPixel.b, currentPixel.g, currentPixel.r); - - // 计算颜色调整 - const float alpha = 0.3f; // 调整强度 - cv::Vec3f adjustedColor = currentColor; - - // 如果颜色差异较大,向邻域均值调整 - cv::Vec3f diff = currentColor - mean; - float diffMag = std::sqrt(diff[0]*diff[0] + diff[1]*diff[1] + diff[2]*diff[2]); + cv::Vec3b& detailPixel = detail.at(y, x); + cv::Vec3b blurPixel = blurred.at(y, x); - if (diffMag > 10.0f) { // 颜色差异阈值 - adjustedColor = currentColor * (1.0f - alpha) + mean * alpha; - } + // 计算细节 + detailPixel[0] = cv::saturate_cast(detailPixel[0] - blurPixel[0] + 128); + detailPixel[1] = cv::saturate_cast(detailPixel[1] - blurPixel[1] + 128); + detailPixel[2] = cv::saturate_cast(detailPixel[2] - blurPixel[2] + 128); + } + } + + // 应用锐化 + float alpha = 0.5f * strength; + for (int y = 0; y < texture.rows; ++y) { + for (int x = 0; x < texture.cols; ++x) { + Pixel8U pixel = texture(y, x); + if (pixel == colEmpty) continue; - // 应用调整 - Pixel8U newPixel; - newPixel.r = (unsigned char)cv::saturate_cast(adjustedColor[2]); - newPixel.g = (unsigned char)cv::saturate_cast(adjustedColor[1]); - newPixel.b = (unsigned char)cv::saturate_cast(adjustedColor[0]); + cv::Vec3b& pixelVec = textureMat.at(y, x); + cv::Vec3b detailPixel = detail.at(y, x); - texture(y, x) = newPixel; + pixelVec[0] = cv::saturate_cast(pixelVec[0] + (detailPixel[0] - 128) * alpha); + pixelVec[1] = cv::saturate_cast(pixelVec[1] + (detailPixel[1] - 128) * alpha); + pixelVec[2] = cv::saturate_cast(pixelVec[2] + (detailPixel[2] - 128) * alpha); } } } -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几何坐标和多视图融合"); +// 新增辅助函数实现 +void MeshTexture::BuildFaceNeighbors(std::vector>& faceNeighbors) const { + const Mesh& mesh = scene.mesh; + const int numFaces = (int)mesh.faces.size(); - // 定义INVALID_INDEX常量 - const IIndex INVALID_INDEX = (IIndex)-1; + faceNeighbors.resize(numFaces); - // 1. 分析外部UV布局 - AABB2f uvBounds(true); - FOREACH(i, scene.mesh.faceTexcoords) { - const TexCoord& uv = scene.mesh.faceTexcoords[i]; - uvBounds.InsertFull(uv); + // 构建边到面的映射 + std::unordered_map> edgeFaces; + + for (int fid = 0; fid < numFaces; ++fid) { + const Face& face = mesh.faces[fid]; + + // 三条边 + for (int i = 0; i < 3; ++i) { + int v1 = face[i]; + int v2 = face[(i + 1) % 3]; + + // 确保边有序 + if (v1 > v2) std::swap(v1, v2); + + uint64_t edgeKey = ((uint64_t)v1 << 32) | v2; + edgeFaces[edgeKey].push_back(fid); + } } - // 确保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); + // 构建相邻面关系 + for (const auto& pair : edgeFaces) { + const std::vector& faceList = pair.second; + if (faceList.size() >= 2) { + // 边的相邻面 + for (size_t i = 0; i < faceList.size(); ++i) { + for (size_t j = i + 1; j < faceList.size(); ++j) { + int fid1 = faceList[i]; + int fid2 = faceList[j]; + + faceNeighbors[fid1].push_back(fid2); + faceNeighbors[fid2].push_back(fid1); + } } } } - // 计算纹理尺寸 - 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); + // 去重 + for (int fid = 0; fid < numFaces; ++fid) { + auto& neighbors = faceNeighbors[fid]; + std::sort(neighbors.begin(), neighbors.end()); + neighbors.erase(std::unique(neighbors.begin(), neighbors.end()), neighbors.end()); + } - // 创建目标纹理 - Mesh::Image8U3Arr textures; - Image8U3& textureAtlas = textures.emplace_back(textureSize, textureSize); + DEBUG_EXTRA("相邻面关系构建完成: 共 %d 个面", numFaces); +} + +void MeshTexture::OptimizeViewSelectionWithGraphCut(LabelArr& faceLabels, const std::vector>& faceNeighbors) { + DEBUG_EXTRA("优化视图选择以减少色块"); - // 使用统一的背景色设置 - DEBUG_EXTRA("设置背景色: RGB(%d,%d,%d)", colEmpty.r, colEmpty.g, colEmpty.b); - cv::Scalar cvEmpty(colEmpty.b, colEmpty.g, colEmpty.r); - textureAtlas.setTo(cvEmpty); + if (faceLabels.empty() || faceNeighbors.empty()) return; - DEBUG_EXTRA("生成纹理图集: 尺寸=%dx%d, UV范围=[%.3f,%.3f]-[%.3f,%.3f]", - textureSize, textureSize, - uvBounds.ptMin.x(), uvBounds.ptMin.y(), - uvBounds.ptMax.x(), uvBounds.ptMax.y()); + const float smoothnessWeight = 1.0f; // 平滑项权重 + const float dataWeight = 0.5f; // 数据项权重 - std::vector> pixelSamples(textureSize * textureSize); + int iterationCount = 0; + int changedCount = 0; + + do { + changedCount = 0; + iterationCount++; + + // 并行处理每个面 + #ifdef _USE_OPENMP + #pragma omp parallel for schedule(dynamic) reduction(+:changedCount) + #endif + for (int_t idxFace = 0; idxFace < (int_t)scene.mesh.faces.size(); ++idxFace) { + FIndex faceID = (FIndex)idxFace; + Label currentLabel = faceLabels[faceID]; + + if (currentLabel == 0) continue; + + IIndex currentView = currentLabel - 1; + float currentEnergy = 0.0f; + + // 平滑项:相邻面的视图一致性 + float smoothEnergy = 0.0f; + int neighborCount = 0; + + for (int neighborID : faceNeighbors[faceID]) { + if (neighborID < 0 || neighborID >= (int)faceLabels.size()) continue; + + Label neighborLabel = faceLabels[neighborID]; + if (neighborLabel == 0) continue; + + if (currentLabel != neighborLabel) { + smoothEnergy += 1.0f; // 视图不同增加能量 + } + neighborCount++; + } + + if (neighborCount > 0) { + smoothEnergy /= neighborCount; + currentEnergy += smoothnessWeight * smoothEnergy; + } + + // 尝试相邻面的视图 + std::vector