diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 0cb1a85..757a2f2 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -346,6 +346,43 @@ struct MeshTexture { float score; }; + struct TextureGenerationConfig { + // 视图选择参数 + float viewAngleWeight = 0.4f; + float viewDistanceWeight = 0.3f; + float viewResolutionWeight = 0.3f; + + // 颜色融合参数 + float colorDiffThreshold = 20.0f; + float auxiliaryViewScale = 0.3f; + float mainViewDominance = 0.7f; + + // 平滑参数 + int smoothingIterations = 2; + float smoothingStrength = 0.5f; + + // 高级参数 + bool useColorConsistency = true; + bool useViewWeights = true; + bool fillTextureGaps = true; + }; + + // 采样数据结构 + struct TextureSample { + cv::Vec3f color; // BGR颜色 + float weight; // 采样权重 + IIndex viewIdx; // 视图索引 + Point2f imageCoord; // 图像坐标 + Vertex worldCoord; // 世界坐标 + }; + + // 面片数据 + struct FaceData2 { + std::vector samples; + cv::Vec3f averageColor; + float confidence; + }; + // used to optimize texture patches struct SeamVertex { struct Patch { @@ -606,6 +643,38 @@ public: void OptimizeTextureSeams(const std::vector& textures, const std::vector>& seamPoints); + void BuildFaceViewVisibility(const IIndexArr& views, + const FaceDataViewArr& facesDatas, + std::vector>& faceVisibleViews); + + float ComputeViewScore(const Image& image, + const Vertex& faceCenter, + const Vertex& faceNormal, + float maxDistance = 100.0f); + + cv::Vec3f SmartColorBlend(const std::vector& samples, + const cv::Vec3f& fallbackColor); + + void BuildFaceSamples(FIndex idxFace, + const LabelArr& faceLabels, + std::vector& faceSamplesData, + int textureSize); + + void GenerateTextureAtlasOptimized(const std::vector& faceSamplesData, + Mesh::Image8U3Arr& textures, + int textureSize, + const Pixel8U& colEmpty); + + void ApplyMultiViewColorOptimization(Image8U3& texture, + const std::vector& faceSamplesData, + const Pixel8U& colEmpty); + + void ApplyGradientDomainOptimization(Image8U3& texture, + const Pixel8U& colEmpty); + + void ApplyTextureSeamBlending(Image8U3& texture, + const Pixel8U& colEmpty); + void CheckColorChannels(const Image8U3& texture, const std::string& name); Mesh::Image8U3Arr GenerateTextureAtlasWith3DBridge( const LabelArr& faceLabels, @@ -618,7 +687,7 @@ public: float fSharpnessWeight); float ComputeViewWeight(const Image& image, const Vertex& faceCenter, const Vertex& normal); float ComputeProjectedArea(const Vertex& faceCenter, const Vertex& normal, const Camera& camera); - void FillTextureGaps(Image8U3& texture, Pixel8U colEmpty); + void FillTextureGaps2(Image8U3& texture, const Pixel8U& colEmpty, int patchSize); void ApplyColorConsistencyOptimization( Image8U3& texture, const std::vector>& pixelSamples, @@ -13063,6 +13132,536 @@ void MeshTexture::OptimizeTextureSeams(const std::vector& textures, DEBUG_EXTRA("Seam optimization completed"); } + +// 1. 改进的视图分数计算 +float MeshTexture::ComputeViewScore(const Image& image, + const Vertex& faceCenter, + const Vertex& faceNormal, + float maxDistance) +{ + // 相机位置 + Vertex cameraPos(image.camera.C.x, image.camera.C.y, image.camera.C.z); + + // 1. 距离计算 + Vertex viewVector = cameraPos - faceCenter; + float distance = cv::norm(viewVector); + + if (distance < 1e-6f) return 0.0f; + + // 归一化视角向量 + Vertex viewDir = viewVector / distance; + + // 2. 视角角度分数 (cosθ, 越大越好) + float cosAngle = std::abs(viewDir.dot(faceNormal)); + if (cosAngle < 0.0f) return 0.0f; // 背面 + + // 角度分数: 非线性映射,接近0度时分数高 + float angleScore = cosAngle * cosAngle; // 使用平方强化 + + // 3. 距离分数: 指数衰减 + float normalizedDist = std::min(distance / maxDistance, 1.0f); + float distanceScore = std::exp(-3.0f * normalizedDist); + + // 4. 分辨率分数: 基于投影面积 + float resolutionScore = 1.0f; + if (image.camera.IsInFront(faceCenter)) { + // 计算面在图像中的近似大小 + float projectionSize = 1.0f / (distance + 1.0f); + resolutionScore = std::min(projectionSize * 100.0f, 1.0f); + } + + // 5. 边缘惩罚: 靠近图像边缘的视图分数降低 + Point2f proj = image.camera.ProjectPointP(faceCenter); + float cx = proj.x / image.image.cols; + float cy = proj.y / image.image.rows; + float edgeDistance = std::min(std::min(cx, 1.0f - cx), std::min(cy, 1.0f - cy)); + float edgeScore = 1.0f - std::exp(-5.0f * edgeDistance); // 边缘分数 + + // 综合分数 + float finalScore = (angleScore * 0.4f) + + (distanceScore * 0.2f) + + (resolutionScore * 0.2f) + + (edgeScore * 0.2f); + + return std::max(0.0f, std::min(1.0f, finalScore)); +} + +// 2. 构建面片可见视图 +void MeshTexture::BuildFaceViewVisibility(const IIndexArr& views, + const FaceDataViewArr& facesDatas, + std::vector>& faceVisibleViews) +{ + DEBUG_EXTRA("构建面片可见视图关系"); + faceVisibleViews.resize(scene.mesh.faces.size()); + + #ifdef _USE_OPENMP + #pragma omp parallel for schedule(dynamic) + #endif + for (int_t idxFace = 0; idxFace < (int_t)scene.mesh.faces.size(); ++idxFace) { + FIndex faceID = (FIndex)idxFace; + const Face& face = scene.mesh.faces[faceID]; + + // 计算面法线 + Vertex v0 = vertices[face[1]] - vertices[face[0]]; + Vertex v1 = vertices[face[2]] - vertices[face[0]]; + Vertex normal = v0.cross(v1); + double normVal = cv::norm(normal); + if (normVal > 0) { + normal = normal / (float)normVal; + } else { + normal = Vertex(0, 0, 1); + } + + // 计算面中心 + Vertex faceCenter(0, 0, 0); + for (int v = 0; v < 3; ++v) { + faceCenter += vertices[face[v]]; + } + faceCenter /= 3.0f; + + // 收集所有可见视图及其分数 + std::vector> viewScores; + + for (IIndex i = 0; i < (IIndex)images.size(); ++i) { + const Image& img = images[i]; + + // 基本可见性检查 + if (!img.camera.IsInFront(faceCenter)) continue; + + // 计算投影点 + Point2f imgPoint = img.camera.ProjectPointP(faceCenter); + if (imgPoint.x < 0 || imgPoint.x >= img.image.cols || + imgPoint.y < 0 || imgPoint.y >= img.image.rows) { + continue; + } + + // 计算视图分数 + float score = ComputeViewScore(img, faceCenter, normal); + + if (score > 0.1f) { // 分数阈值 + viewScores.push_back({score, i}); + } + } + + // 按分数排序 + std::sort(viewScores.begin(), viewScores.end(), + [](const auto& a, const auto& b) { return a.first > b.first; }); + + // 选择最佳视图(最多5个) + auto& visible = faceVisibleViews[idxFace]; + for (size_t i = 0; i < std::min(viewScores.size(), (size_t)5); ++i) { + visible.push_back(viewScores[i].second); + } + + if (visible.empty()) { + DEBUG_EXTRA("面片 %d 没有可见视图", faceID); + } + } +} + +// 3. 智能颜色融合 +cv::Vec3f MeshTexture::SmartColorBlend(const std::vector& samples, + const cv::Vec3f& fallbackColor) +{ + if (samples.empty()) return fallbackColor; + if (samples.size() == 1) return samples[0].color; + + // 按视图分组 + std::unordered_map>> viewGroups; + for (const auto& sample : samples) { + viewGroups[sample.viewIdx].push_back({sample.color, sample.weight}); + } + + // 计算每个视图的平均颜色 + std::vector viewColors; + std::vector viewWeights; + + for (const auto& group : viewGroups) { + cv::Vec3f sumColor(0, 0, 0); + float sumWeight = 0.0f; + + for (const auto& sample : group.second) { + sumColor += sample.first * sample.second; + sumWeight += sample.second; + } + + if (sumWeight > 0) { + viewColors.push_back(sumColor / sumWeight); + viewWeights.push_back(sumWeight); + } + } + + if (viewColors.size() == 1) return viewColors[0]; + + // 查找主视图(权重最大的视图) + int mainIdx = 0; + float maxWeight = 0.0f; + for (size_t i = 0; i < viewWeights.size(); ++i) { + if (viewWeights[i] > maxWeight) { + maxWeight = viewWeights[i]; + mainIdx = i; + } + } + + // 计算主视图与其他视图的颜色一致性 + std::vector consistentColors; + std::vector consistentWeights; + + cv::Vec3f mainColor = viewColors[mainIdx]; + consistentColors.push_back(mainColor); + consistentWeights.push_back(viewWeights[mainIdx]); + + for (size_t i = 0; i < viewColors.size(); ++i) { + if (i == (size_t)mainIdx) continue; + + cv::Vec3f diff = viewColors[i] - mainColor; + float diffMag = std::sqrt(diff.dot(diff)); + + // 颜色差异阈值自适应调整 + float adaptiveThreshold = 15.0f + (mainColor[0] + mainColor[1] + mainColor[2]) / 30.0f; + + if (diffMag < adaptiveThreshold) { + // 颜色一致,可以融合 + float blendWeight = viewWeights[i] * (1.0f - diffMag / adaptiveThreshold); + consistentColors.push_back(viewColors[i]); + consistentWeights.push_back(blendWeight); + } + } + + // 计算加权平均 + cv::Vec3f result(0, 0, 0); + float totalWeight = 0.0f; + + for (size_t i = 0; i < consistentColors.size(); ++i) { + result += consistentColors[i] * consistentWeights[i]; + totalWeight += consistentWeights[i]; + } + + if (totalWeight > 0) { + result /= totalWeight; + } else { + result = mainColor; + } + + return result; +} + +// 4. 构建面片采样数据 +void MeshTexture::BuildFaceSamples(FIndex idxFace, + const LabelArr& faceLabels, + std::vector& faceSamplesData, + int textureSize) +{ + const Face& face = scene.mesh.faces[idxFace]; + const TexCoord* meshUVs = &scene.mesh.faceTexcoords[idxFace * 3]; + Label label = faceLabels[idxFace]; + + FaceData2 faceData; + faceData.confidence = 0.0f; + + if (label == 0) { + faceSamplesData[idxFace] = faceData; + return; + } + + // 计算面法线 + Vertex v0 = vertices[face[1]] - vertices[face[0]]; + Vertex v1 = vertices[face[2]] - vertices[face[0]]; + Vertex normal = v0.cross(v1); + double normVal = cv::norm(normal); + if (normVal > 0) { + normal = normal / (float)normVal; + } else { + normal = Vertex(0, 0, 1); + } + + // 计算面中心 + Vertex faceCenter(0, 0, 0); + for (int v = 0; v < 3; ++v) { + faceCenter += vertices[face[v]]; + } + faceCenter /= 3.0f; + + // 计算UV边界 + AABB2f uvBounds(true); + for (int i = 0; i < 3; ++i) { + uvBounds.InsertFull(meshUVs[i]); + } + + int startX = (int)(uvBounds.ptMin.x() * textureSize); + int startY = (int)(uvBounds.ptMin.y() * textureSize); + int endX = (int)(uvBounds.ptMax.x() * textureSize); + int endY = (int)(uvBounds.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) { + faceSamplesData[idxFace] = faceData; + return; + } + + // 收集采样 + std::vector samples; + + for (int y = startY; y <= endY; ++y) { + for (int x = startX; x <= endX; ++x) { + Point2f texCoord((x + 0.5f) / textureSize, (y + 0.5f) / textureSize); + + // 计算重心坐标 + Point3f barycentric; + if (!PointInTriangle(texCoord, meshUVs[0], meshUVs[1], meshUVs[2], barycentric)) { + continue; + } + + // 计算3D点 + Vertex worldPoint = vertices[face[0]] * barycentric.x + + vertices[face[1]] * barycentric.y + + vertices[face[2]] * barycentric.z; + + // 主视图采样 + IIndex primaryView = label - 1; + if (primaryView < (IIndex)images.size()) { + const Image& img = images[primaryView]; + + if (img.camera.IsInFront(worldPoint)) { + Point2f imgPoint = img.camera.ProjectPointP(worldPoint); + if (imgPoint.x >= 0 && imgPoint.x < img.image.cols && + imgPoint.y >= 0 && imgPoint.y < img.image.rows) { + + Pixel8U color = SampleImageBilinear(img.image, imgPoint); + float weight = ComputeViewScore(img, faceCenter, normal); + + TextureSample sample; + sample.color = cv::Vec3f(color.b, color.g, color.r); + sample.weight = weight; + sample.viewIdx = primaryView; + sample.imageCoord = imgPoint; + sample.worldCoord = worldPoint; + + samples.push_back(sample); + } + } + } + } + } + + // 计算平均颜色和置信度 + if (!samples.empty()) { + cv::Vec3f sumColor(0, 0, 0); + float sumWeight = 0.0f; + + for (const auto& sample : samples) { + sumColor += sample.color * sample.weight; + sumWeight += sample.weight; + } + + faceData.samples = samples; + faceData.averageColor = sumColor / sumWeight; + faceData.confidence = sumWeight / samples.size(); // 平均权重作为置信度 + } + + faceSamplesData[idxFace] = faceData; +} + +// 5. 优化的纹理图集生成 +void MeshTexture::GenerateTextureAtlasOptimized(const std::vector& faceSamplesData, + Mesh::Image8U3Arr& textures, + int textureSize, + const Pixel8U& colEmpty) +{ + DEBUG_EXTRA("生成优化纹理图集: 尺寸=%dx%d", textureSize, textureSize); + + Image8U3& textureAtlas = textures.emplace_back(textureSize, textureSize); + cv::Scalar cvEmpty(colEmpty.b, colEmpty.g, colEmpty.r); + textureAtlas.setTo(cvEmpty); + + // 第一遍: 填充基本颜色 + cv::Mat3f colorAccum(textureSize, textureSize, cv::Vec3f(0, 0, 0)); + cv::Mat1f weightAccum(textureSize, textureSize, 0.0f); + cv::Mat1i sampleCount(textureSize, textureSize, 0); + + for (FIndex idxFace = 0; idxFace < scene.mesh.faces.size(); ++idxFace) { + const FaceData2& faceData = faceSamplesData[idxFace]; + if (faceData.samples.empty()) continue; + + const TexCoord* meshUVs = &scene.mesh.faceTexcoords[idxFace * 3]; + + // 计算UV边界 + AABB2f uvBounds(true); + for (int i = 0; i < 3; ++i) { + uvBounds.InsertFull(meshUVs[i]); + } + + int startX = (int)(uvBounds.ptMin.x() * textureSize); + int startY = (int)(uvBounds.ptMin.y() * textureSize); + int endX = (int)(uvBounds.ptMax.x() * textureSize); + int endY = (int)(uvBounds.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)); + + for (int y = startY; y <= endY; ++y) { + for (int x = startX; x <= endX; ++x) { + Point2f texCoord((x + 0.5f) / textureSize, (y + 0.5f) / textureSize); + + // 检查是否在三角形内 + Point3f barycentric; + if (!PointInTriangle(texCoord, meshUVs[0], meshUVs[1], meshUVs[2], barycentric)) { + continue; + } + + // 使用面片的平均颜色 + int pixelIdx = y * textureSize + x; + colorAccum(y, x) += faceData.averageColor * faceData.confidence; + weightAccum(y, x) += faceData.confidence; + sampleCount(y, x)++; + } + } + } + + // 第二遍: 生成纹理 + int filledPixels = 0; + for (int y = 0; y < textureSize; ++y) { + for (int x = 0; x < textureSize; ++x) { + if (weightAccum(y, x) > 0) { + cv::Vec3f avgColor = colorAccum(y, x) / weightAccum(y, x); + + Pixel8U pixel; + pixel.b = (unsigned char)cv::saturate_cast(avgColor[0]); + pixel.g = (unsigned char)cv::saturate_cast(avgColor[1]); + pixel.r = (unsigned char)cv::saturate_cast(avgColor[2]); + + textureAtlas(y, x) = pixel; + filledPixels++; + } + } + } + + DEBUG_EXTRA("基本纹理填充完成: %d/%d 像素 (%.1f%%)", + filledPixels, textureSize * textureSize, + (float)filledPixels * 100 / (textureSize * textureSize)); +} + +void MeshTexture::ApplyGradientDomainOptimization(Image8U3& texture, + const Pixel8U& colEmpty) +{ + DEBUG_EXTRA("应用梯度域优化(轻量级版本)"); + + if (texture.empty()) { + DEBUG_EXTRA("警告: 纹理为空"); + return; + } + + int rows = texture.rows; + int cols = texture.cols; + int totalPixels = rows * cols; + + DEBUG_EXTRA("开始梯度域优化: 图像大小=%dx%d, 总像素=%d", cols, rows, totalPixels); + + // 1. 创建掩码 + cv::Mat mask(rows, cols, CV_8U, cv::Scalar(0)); + int validCount = 0; + + for (int y = 0; y < rows; ++y) { + const cv::Vec3b* row = texture.ptr(y); + uchar* maskRow = mask.ptr(y); + + for (int x = 0; x < cols; ++x) { + const cv::Vec3b& pixel = row[x]; + if (pixel != cv::Vec3b(colEmpty.b, colEmpty.g, colEmpty.r)) { + maskRow[x] = 255; + validCount++; + } + } + } + + DEBUG_EXTRA("有效像素: %d/%d (%.1f%%)", validCount, totalPixels, + validCount * 100.0f / totalPixels); + + if (validCount < 1000) { + DEBUG_EXTRA("有效像素太少,跳过优化"); + return; + } + + // 2. 分别处理每个通道 + for (int channel = 0; channel < 3; channel++) { + DEBUG_EXTRA("处理通道 %d", channel); + + // 提取单个通道 + std::vector channels; + cv::split(texture, channels); + cv::Mat singleChannel = channels[channel]; + + // 转换为浮点 + cv::Mat floatChannel; + singleChannel.convertTo(floatChannel, CV_32F); + + // 3. 使用高斯模糊替代拉普拉斯 + cv::Mat blurred; + cv::GaussianBlur(floatChannel, blurred, cv::Size(3, 3), 1.0); + + // 4. 计算拉普拉斯算子(使用更小的核) + cv::Mat laplacian; + cv::Laplacian(floatChannel, laplacian, CV_32F, 1); + + // 5. 应用梯度平滑 + int processedPixels = 0; + float smoothingFactor = 0.1f; // 降低平滑强度 + + for (int y = 0; y < rows; ++y) { + float* lapRow = laplacian.ptr(y); + float* blurRow = blurred.ptr(y); + float* origRow = floatChannel.ptr(y); + const uchar* maskRow = mask.ptr(y); + + for (int x = 0; x < cols; ++x) { + if (maskRow[x]) { + // 简单的梯度平滑 + origRow[x] = origRow[x] - smoothingFactor * lapRow[x]; + processedPixels++; + + // 每处理100000个像素报告一次进度 + if (processedPixels % 100000 == 0) { + DEBUG_EXTRA(" 通道 %d: 已处理 %d/%d 像素 (%.1f%%)", + channel, processedPixels, validCount, + processedPixels * 100.0f / validCount); + } + } + } + } + + DEBUG_EXTRA("通道 %d 处理完成: %d 像素", channel, processedPixels); + + // 限制值范围 + cv::Mat clamped; + cv::threshold(floatChannel, clamped, 0, 0, cv::THRESH_TOZERO); + cv::threshold(clamped, clamped, 255, 255, cv::THRESH_TRUNC); + + // 转回8位 + clamped.convertTo(singleChannel, CV_8U); + + channels[channel] = singleChannel; + + // 释放内存 + floatChannel.release(); + blurred.release(); + laplacian.release(); + } + + // 6. 合并通道 + cv::Mat result; + std::vector channels; + cv::split(texture, channels); + cv::merge(channels, texture); + + DEBUG_EXTRA("梯度域优化完成"); +} + std::pair MeshTexture::FindSharedEdgeIndices(const Face& face0, const Face& face1) { // 找到两个面共享的边 @@ -13651,71 +14250,95 @@ bool MeshTexture::TextureWithExistingUV( float fSharpnessWeight, const Mesh::Image8U3Arr& existingTextures, const Mesh::TexCoordArr& existingTexcoords, - const Mesh::TexIndexArr& existingTexindices) + const Mesh::TexIndexArr& existingTexindices) { - DEBUG_EXTRA("TextureWithExistingUV - 使用3D几何坐标作为桥梁"); - TD_TIMER_START(); + DEBUG_EXTRA("TextureWithExistingUV - 使用3D几何坐标和多视图融合"); - // 1. 验证输入 if (scene.mesh.faceTexcoords.empty()) { - VERBOSE("error: mesh does not contain UV coordinates"); + VERBOSE("错误: 网格没有UV坐标"); 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. 为每个面选择最佳视图(从原始图像,而不是已有纹理) + // 1. 为每个面选择最佳视图 FaceDataViewArr facesDatas; if (!ListCameraFaces(facesDatas, fOutlierThreshold, nIgnoreMaskLabel, views, false)) { + VERBOSE("错误: 无法列举相机-面片关系"); return false; } - // 3. 为每个面分配最佳视图 + // 2. 分配面片标签 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; + std::vector faceConfidences(scene.mesh.faces.size(), 0.0f); + + #ifdef _USE_OPENMP + #pragma omp parallel for schedule(dynamic) + #endif + for (int_t idxFace = 0; idxFace < (int_t)scene.mesh.faces.size(); ++idxFace) { + FIndex faceID = (FIndex)idxFace; + const FaceDataArr& faceDatas = facesDatas[faceID]; + + if (faceDatas.empty()) { + faceLabels[faceID] = 0; + faceConfidences[faceID] = 0.0f; + continue; + } + + // 计算面法线 + const Face& face = scene.mesh.faces[faceID]; + Vertex v0 = vertices[face[1]] - vertices[face[0]]; + Vertex v1 = vertices[face[2]] - vertices[face[0]]; + Vertex normal = v0.cross(v1); + double normVal = cv::norm(normal); + if (normVal > 0) normal = normal / (float)normVal; + else normal = Vertex(0, 0, 1); + + // 计算面中心 + Vertex faceCenter(0, 0, 0); + for (int v = 0; v < 3; ++v) faceCenter += vertices[face[v]]; + faceCenter /= 3.0f; + + // 选择最佳视图 IIndex bestView = NO_ID; + float bestScore = -1.0f; + for (const FaceData& data : faceDatas) { - if (data.quality > bestQuality && !data.bInvalidFacesRelative) { - bestQuality = data.quality; + if (data.bInvalidFacesRelative) continue; + + float score = ComputeViewScore(images[data.idxView], faceCenter, normal); + if (score > bestScore) { + bestScore = score; bestView = data.idxView; } } - faceLabels[idxFace] = (bestView != NO_ID) ? (bestView + 1) : 0; + if (bestView != NO_ID && bestScore > 0.1f) { + faceLabels[faceID] = bestView + 1; + faceConfidences[faceID] = bestScore; + } else { + faceLabels[faceID] = 0; + faceConfidences[faceID] = 0.0f; + } + } + + // 3. 统计 + int labeledFaces = 0; + for (Label label : faceLabels) { + if (label > 0) labeledFaces++; } - // 4. 生成纹理图集 + DEBUG_EXTRA("面片标签分配完成: %d/%d (%.1f%%) 个面有视图", + labeledFaces, scene.mesh.faces.size(), + (float)labeledFaces * 100 / scene.mesh.faces.size()); + + // 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) { @@ -13725,49 +14348,14 @@ bool MeshTexture::TextureWithExistingUV( scene.mesh.faceTexindices.Release(); } - DEBUG_EXTRA("Generated %zu textures from existing data", - scene.mesh.texturesDiffuse.size()); + DEBUG_EXTRA("纹理生成成功: %zu 个纹理", scene.mesh.texturesDiffuse.size()); return true; } - DEBUG_EXTRA("Texture generation failed"); + DEBUG_EXTRA("纹理生成失败"); return false; } -void MeshTexture::FillTextureGaps(Image8U3& texture, Pixel8U colEmpty) { - if (texture.empty()) return; - - cv::Mat textureMat = (cv::Mat&)texture; - - // 创建掩码 - 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; - } - } - } - - // 使用图像修复算法填充空隙 - cv::Mat inpainted; - cv::inpaint(textureMat, mask, inpainted, 3, cv::INPAINT_NS); - - // 只替换空区域 - 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; - } - } - } -} - void MeshTexture::ApplyColorConsistencyOptimization( Image8U3& texture, const std::vector>& pixelSamples, @@ -13916,610 +14504,83 @@ Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasWith3DBridge( const Mesh::TexIndexArr& sourceTexindices, unsigned nTextureSizeMultiple, Pixel8U colEmpty, - float fSharpnessWeight) + float fSharpnessWeight) { - DEBUG_EXTRA("GenerateTextureAtlasWith3DBridge - 使用3D几何坐标和多视图融合"); - - // 定义INVALID_INDEX常量 - const IIndex INVALID_INDEX = (IIndex)-1; + DEBUG_EXTRA("优化版纹理图集生成 - 多视图融合 + 颜色一致性"); + TD_TIMER_START(); - // 1. 分析外部UV布局 + // 1. 计算纹理尺寸 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); - - // 使用统一的背景色设置 - DEBUG_EXTRA("设置背景色: RGB(%d,%d,%d)", colEmpty.r, colEmpty.g, colEmpty.b); - cv::Scalar cvEmpty(colEmpty.b, colEmpty.g, colEmpty.r); - textureAtlas.setTo(cvEmpty); + float uvWidth = uvBounds.ptMax.x() - uvBounds.ptMin.x(); + float uvHeight = uvBounds.ptMax.y() - uvBounds.ptMin.y(); + int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple); - DEBUG_EXTRA("生成纹理图集: 尺寸=%dx%d, UV范围=[%.3f,%.3f]-[%.3f,%.3f]", - textureSize, textureSize, - uvBounds.ptMin.x(), uvBounds.ptMin.y(), + DEBUG_EXTRA("UV边界: [%.3f,%.3f] -> [%.3f,%.3f]", + uvBounds.ptMin.x(), uvBounds.ptMin.y(), uvBounds.ptMax.x(), uvBounds.ptMax.y()); + DEBUG_EXTRA("纹理尺寸: %dx%d", textureSize, textureSize); - std::vector> pixelSamples(textureSize * textureSize); - - // 3. 第一次遍历:收集所有视图的采样 - DEBUG_EXTRA("第一次遍历:收集多视图采样"); - int totalSamples = 0; + // 2. 构建面片可见视图 + std::vector> faceVisibleViews; + FaceDataViewArr facesDatas; - #ifdef _USE_OPENMP - #pragma omp parallel for schedule(dynamic) reduction(+:totalSamples) - #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) continue; - const IIndex primaryView = label - 1; - if (primaryView >= images.size()) continue; - - const TexCoord* meshUVs = &scene.mesh.faceTexcoords[faceID * 3]; - const Face& face = scene.mesh.faces[faceID]; - const Image& sourceImage = images[primaryView]; - - // 计算面法线 - Vertex v0 = vertices[face[1]] - vertices[face[0]]; - Vertex v1 = vertices[face[2]] - vertices[face[0]]; - Vertex crossProduct = v0.cross(v1); - double normVal = cv::norm(crossProduct); - Vertex normal(0, 0, 1); // 默认法向量 - if (normVal > 0) { - normal = crossProduct / (float)normVal; // 归一化 - } - - // 计算面的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) continue; - - // 采样密度 - const int sampleStep = 1; // 每个像素都采样 - int faceSamples = 0; - - for (int y = startY; y <= endY; y += sampleStep) { - for (int x = startX; x <= endX; x += sampleStep) { - const Point2f texCoord((x + 0.5f) / textureSize, (y + 0.5f) / textureSize); - - // 计算重心坐标 - Point3f barycentric; - if (!PointInTriangle(texCoord, meshUVs[0], meshUVs[1], meshUVs[2], barycentric)) { - continue; - } - - // 计算3D点 - const Vertex worldPoint = - vertices[face[0]] * barycentric.x + - vertices[face[1]] * barycentric.y + - vertices[face[2]] * barycentric.z; - - // 检查3D点是否在相机前方 - if (!sourceImage.camera.IsInFront(worldPoint)) { - continue; - } - - // 投影到图像 - Point2f imgPoint = sourceImage.camera.ProjectPointP(worldPoint); - - // 确保在图像范围内 - if (imgPoint.x < 0 || imgPoint.x >= sourceImage.image.cols || - imgPoint.y < 0 || imgPoint.y >= sourceImage.image.rows) { - continue; - } - - // 采样图像 - Pixel8U sampledColor = SampleImageBilinear(sourceImage.image, imgPoint); - - // 计算权重 - float weight = 1.0f; // 默认权重 - - // 计算第一个顶点在图像上的投影(用于分辨率计算) - Point2f proj0 = sourceImage.camera.ProjectPointP(vertices[face[0]]); - - // 计算到相机中心的距离 - Vertex cameraPos(sourceImage.camera.C.x, sourceImage.camera.C.y, sourceImage.camera.C.z); - float dist = cv::norm(cameraPos - worldPoint); - - // 计算视角与法线夹角 - Vertex viewDir = cameraPos - worldPoint; - float viewDirNorm = cv::norm(viewDir); - if (viewDirNorm > 0) { - viewDir = viewDir / viewDirNorm; - float viewAngle = std::abs(viewDir.dot(normal)); - - // 计算分辨率 - float resolution = 1.0f; - double projDist = cv::norm(imgPoint - proj0); - if (projDist > 1e-6) { - resolution = 1.0f / (float)projDist; - } - - // 综合权重 = 视角质量 * 分辨率 - weight = viewAngle * resolution; - - // 添加距离衰减 - float distFactor = std::exp(-dist * 0.001f); - weight *= distFactor; - - // 限制权重范围 - weight = std::max(0.5f, std::min(2.0f, weight)); - } - - // 存储采样 - int pixelIdx = y * textureSize + x; - PixelSample sample; - sample.color = cv::Vec3f(sampledColor.b, sampledColor.g, sampledColor.r); // 转换为BGR - sample.weight = weight; - sample.viewIdx = primaryView; - - #ifdef _USE_OPENMP - #pragma omp critical - #endif - { - pixelSamples[pixelIdx].push_back(sample); - } - - faceSamples++; - } - } - - totalSamples += faceSamples; + if (!ListCameraFaces(facesDatas, 0.6f, -1, views, false)) { + DEBUG_EXTRA("错误: 无法构建相机-面片关系"); + return Mesh::Image8U3Arr(); } - DEBUG_EXTRA("采样完成: 总采样点 %d", totalSamples); - - // 4. 第二次遍历:从其他视图补充采样 - DEBUG_EXTRA("第二次遍历:从其他视图补充采样"); + BuildFaceViewVisibility(views, facesDatas, faceVisibleViews); - // 找出每个面的其他可见视图 - std::vector> faceVisibleViews(scene.mesh.faces.size()); + // 3. 构建面片采样数据 + DEBUG_EXTRA("构建面片采样数据..."); + std::vector faceSamplesData(scene.mesh.faces.size()); - std::vector faceNormals(scene.mesh.faces.size()); - #ifdef _USE_OPENMP #pragma omp parallel for schedule(dynamic) #endif for (int_t idxFace = 0; idxFace < (int_t)scene.mesh.faces.size(); ++idxFace) { - const FIndex faceID = (FIndex)idxFace; - const Face& face = scene.mesh.faces[faceID]; - - // 计算面法线 - Vertex v0 = vertices[face[1]] - vertices[face[0]]; - Vertex v1 = vertices[face[2]] - vertices[face[0]]; - Vertex crossProduct = v0.cross(v1); - double normVal = cv::norm(crossProduct); - Vertex normal(0, 0, 1); // 默认法向量 - if (normVal > 0) { - normal = crossProduct / (float)normVal; // 归一化 - } - - if (normVal > 0) { - faceNormals[idxFace] = crossProduct / (float)normVal; // 归一化 - } else { - faceNormals[idxFace] = Vertex(0, 0, 1); // 默认法向量 - } - - // 计算面中心 - Vertex faceCenter(0, 0, 0); - for (int v = 0; v < 3; ++v) { - faceCenter += vertices[face[v]]; - } - faceCenter /= 3.0f; - - // 检查所有视图 - std::vector> viewScores; - for (IIndex i = 0; i < images.size(); ++i) { - const Image& img = images[i]; - - // 检查可见性 - if (!img.camera.IsInFront(faceCenter)) continue; - - // 计算投影点 - Point2f imgPoint = img.camera.ProjectPointP(faceCenter); - if (imgPoint.x < 0 || imgPoint.x >= img.image.cols || - imgPoint.y < 0 || imgPoint.y >= img.image.rows) { - continue; - } - - // 计算视角方向 - Vertex cameraPos(img.camera.C.x, img.camera.C.y, img.camera.C.z); - Vertex viewDirVec = cameraPos - faceCenter; - float viewDirNorm = cv::norm(viewDirVec); - - if (viewDirNorm <= 1e-6) continue; // 避免除以零 - - Vertex viewDir = viewDirVec / viewDirNorm; - - // 计算视角质量(法线与视角方向的夹角) - float viewAngle = std::abs(viewDir.dot(normal)); - - // 计算分辨率 - 计算两个顶点投影点之间的欧氏距离 - Point2f proj0 = img.camera.ProjectPointP(vertices[face[0]]); - double projDist = cv::norm(imgPoint - proj0); - - // 如果两个投影点太近,则跳过 - if (projDist < 1e-6) continue; - - float resolution = 1.0f / (float)projDist; - - const Vertex& faceNormal = faceNormals[idxFace]; - - // 综合得分 - // float score = viewAngle * resolution; - float score = ComputeViewWeight(images[i], faceCenter, faceNormal); - - if (score > 0.1f) { // 阈值 - viewScores.push_back({score, i}); - } - } - - // 按得分排序 - std::sort(viewScores.begin(), viewScores.end(), - [](const auto& a, const auto& b) { return a.first > b.first; }); - - // 选择前4个最佳视图 - faceVisibleViews[idxFace].push_back(faceLabels[idxFace] - 1); // 主视图 - for (size_t i = 0; i < std::min(viewScores.size(), (size_t)3); ++i) { - if (viewScores[i].second != faceLabels[idxFace] - 1) { - faceVisibleViews[idxFace].push_back(viewScores[i].second); - } - } + BuildFaceSamples((FIndex)idxFace, faceLabels, faceSamplesData, textureSize); } - // 5. 从其他视图采样 - int additionalSamples = 0; - - #ifdef _USE_OPENMP - #pragma omp parallel for schedule(dynamic) reduction(+:additionalSamples) - #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) continue; - - const TexCoord* meshUVs = &scene.mesh.faceTexcoords[faceID * 3]; - const Face& face = scene.mesh.faces[faceID]; - - // 计算面法线 - Vertex v0 = vertices[face[1]] - vertices[face[0]]; - Vertex v1 = vertices[face[2]] - vertices[face[0]]; - Vertex crossProduct = v0.cross(v1); - double normVal = cv::norm(crossProduct); - Vertex normal(0, 0, 1); // 默认法向量 - if (normVal > 0) { - normal = crossProduct / (float)normVal; // 归一化 - } - - // 获取可见视图 - const auto& visibleViews = faceVisibleViews[idxFace]; - if (visibleViews.size() <= 1) continue; // 只有一个视图 - - // 计算面的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) continue; - - // 对每个像素,从其他视图采样 - 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; - } - - // 计算3D点 - const Vertex worldPoint = - vertices[face[0]] * barycentric.x + - vertices[face[1]] * barycentric.y + - vertices[face[2]] * barycentric.z; - - int pixelIdx = y * textureSize + x; - - // 从其他视图采样 - for (size_t i = 1; i < visibleViews.size(); ++i) { // 从第2个视图开始 - IIndex viewIdx = visibleViews[i]; - if (viewIdx >= images.size()) continue; - - const Image& sourceImage = images[viewIdx]; - - // 检查可见性 - if (!sourceImage.camera.IsInFront(worldPoint)) continue; - - // 投影 - Point2f imgPoint = sourceImage.camera.ProjectPointP(worldPoint); - if (imgPoint.x < 0 || imgPoint.x >= sourceImage.image.cols || - imgPoint.y < 0 || imgPoint.y >= sourceImage.image.rows) { - continue; - } - - // 采样图像 - Pixel8U sampledColor = SampleImageBilinear(sourceImage.image, imgPoint); - - // 计算权重 - float weight = 0.2f; // 默认较低权重 - - // 计算第一个顶点在图像上的投影 - Point2f proj0 = sourceImage.camera.ProjectPointP(vertices[face[0]]); - - // 计算投影分辨率 - double projDist = cv::norm(imgPoint - proj0); - float resolution = 1.0f; - if (projDist > 1e-6) { - resolution = 1.0f / (float)projDist; - } - - // 计算视角与法线夹角 - Vertex cameraPos(sourceImage.camera.C.x, sourceImage.camera.C.y, sourceImage.camera.C.z); - Vertex viewDir = cameraPos - worldPoint; - float viewDirNorm = cv::norm(viewDir); - if (viewDirNorm > 0) { - viewDir = viewDir / viewDirNorm; - float viewAngle = std::abs(viewDir.dot(normal)); - - // 综合权重 = 分辨率 * 视角角度 - weight = resolution * viewAngle; - weight = std::max(0.1f, std::min(1.0f, weight * 0.5f)); // 限制权重范围 - } - - // 存储采样 - PixelSample sample; - sample.color = cv::Vec3f(sampledColor.b, sampledColor.g, sampledColor.r); - sample.weight = weight; - sample.viewIdx = viewIdx; - - #ifdef _USE_OPENMP - #pragma omp critical - #endif - { - pixelSamples[pixelIdx].push_back(sample); - additionalSamples++; - } - } - } - } - } - - DEBUG_EXTRA("补充采样完成: 新增采样点 %d", additionalSamples); - - // 6. 融合多视图颜色 - DEBUG_EXTRA("融合多视图颜色"); - cv::Mat3f colorAccum(textureSize, textureSize, cv::Vec3f(0, 0, 0)); - cv::Mat1f weightAccum(textureSize, textureSize, 0.0f); - cv::Mat1i sampleCount(textureSize, textureSize, 0); - int fusedPixels = 0; - - // 添加统计信息 - int mainViewOnly = 0; - int multiViewFused = 0; - int highDiffCount = 0; + // 4. 生成基础纹理 + Mesh::Image8U3Arr textures; + GenerateTextureAtlasOptimized(faceSamplesData, textures, textureSize, colEmpty); - for (int y = 0; y < textureSize; ++y) { - for (int x = 0; x < textureSize; ++x) { - int pixelIdx = y * textureSize + x; - auto& samples = pixelSamples[pixelIdx]; - - if (samples.empty()) continue; - - // 分离主视图和辅助视图 - std::vector mainViewColors; - std::vector mainViewWeights; - std::vector auxiliaryColors; - std::vector auxiliaryWeights; - - IIndex primaryView = INVALID_INDEX; - - for (const auto& sample : samples) { - if (primaryView == INVALID_INDEX) { - primaryView = sample.viewIdx; - } - - if (sample.viewIdx == primaryView) { - // 主视图 - mainViewColors.push_back(sample.color); - mainViewWeights.push_back(sample.weight); - } else { - // 辅助视图 - auxiliaryColors.push_back(sample.color); - auxiliaryWeights.push_back(sample.weight * 0.3f); // 辅助视图权重更低 - } - } - - if (mainViewColors.empty()) continue; - - if (auxiliaryColors.empty()) { - mainViewOnly++; - } else { - multiViewFused++; - } - - // 计算主视图的加权平均颜色 - cv::Vec3f mainColor(0, 0, 0); - float mainWeightSum = 0.0f; - - for (size_t i = 0; i < mainViewColors.size(); ++i) { - mainColor += mainViewColors[i] * mainViewWeights[i]; - mainWeightSum += mainViewWeights[i]; - } - if (mainWeightSum > 0) { - mainColor /= mainWeightSum; - } - - // 如果有辅助视图,进行智能融合 - cv::Vec3f finalColor = mainColor; - float finalWeight = 1.0f; - - if (!auxiliaryColors.empty()) { - // 计算辅助视图的加权平均颜色 - cv::Vec3f auxColor(0, 0, 0); - float auxWeightSum = 0.0f; - - for (size_t i = 0; i < auxiliaryColors.size(); ++i) { - auxColor += auxiliaryColors[i] * auxiliaryWeights[i]; - auxWeightSum += auxiliaryWeights[i]; - } - if (auxWeightSum > 0) { - auxColor /= auxWeightSum; - } - - // 计算颜色差异 - cv::Vec3f colorDiff = mainColor - auxColor; - float colorDiffMag = std::sqrt(colorDiff.dot(colorDiff)); - - // 如果颜色差异不大,才融合辅助视图 - if (colorDiffMag < 30.0f) { // 颜色差异阈值 - // 根据颜色差异调整融合权重 - float alpha = std::exp(-colorDiffMag * 0.1f); // 差异越大,融合越少 - finalColor = mainColor * (1.0f - alpha) + auxColor * alpha; - finalWeight = 1.0f; - } else { - // 颜色差异太大,只使用主视图 - finalColor = mainColor; - finalWeight = 1.0f; - highDiffCount++; - } - } - - // 应用简单的颜色饱和度增强 - float b = finalColor[0]; - float g = finalColor[1]; - float r = finalColor[2]; - - float maxVal = std::max(r, std::max(g, b)); - float minVal = std::min(r, std::min(g, b)); - float delta = maxVal - minVal; - - if (delta > 5.0f) { // 避免对灰度像素处理 - // 增强饱和度 - float saturationBoost = 1.2f; // 20%饱和度增强 - float mean = (r + g + b) / 3.0f; - - r = mean + (r - mean) * saturationBoost; - g = mean + (g - mean) * saturationBoost; - b = mean + (b - mean) * saturationBoost; - - // 限制在有效范围 - r = std::max(0.0f, std::min(255.0f, r)); - g = std::max(0.0f, std::min(255.0f, g)); - b = std::max(0.0f, std::min(255.0f, b)); - - finalColor = cv::Vec3f(b, g, r); - } - - // 存储结果 - colorAccum(y, x) = finalColor; - weightAccum(y, x) = finalWeight; - sampleCount(y, x) = (int)samples.size(); - fusedPixels++; - } - - // 输出进度 - if (y % 100 == 0) { - float progress = (float)y * 100.0f / textureSize; - DEBUG_EXTRA("颜色融合进度: %.1f%% (处理行 %d/%d)", progress, y, textureSize); - } + if (textures.empty()) { + DEBUG_EXTRA("错误: 无法生成纹理"); + return Mesh::Image8U3Arr(); } - DEBUG_EXTRA("颜色融合完成: 融合像素 %d", fusedPixels); - DEBUG_EXTRA("融合统计: 仅主视图=%d, 多视图融合=%d, 高差异=%d", - mainViewOnly, multiViewFused, highDiffCount); + Image8U3& textureAtlas = textures[0]; - // 7. 生成最终纹理 - DEBUG_EXTRA("生成最终纹理"); + // 5. 应用颜色一致性优化 + DEBUG_EXTRA("应用多视图颜色一致性优化..."); + ApplyMultiViewColorOptimization(textureAtlas, faceSamplesData, colEmpty); - for (int y = 0; y < textureSize; ++y) { - for (int x = 0; x < textureSize; ++x) { - float weight = weightAccum(y, x); - if (weight > 0.0f) { - cv::Vec3f bgrColor = colorAccum(y, x); - - // 转换为RGB顺序 - Pixel8U pixel; - pixel.r = (unsigned char)cv::saturate_cast(bgrColor[2]); // R - pixel.g = (unsigned char)cv::saturate_cast(bgrColor[1]); // G - pixel.b = (unsigned char)cv::saturate_cast(bgrColor[0]); // B - - textureAtlas(y, x) = pixel; - } else { - textureAtlas(y, x) = colEmpty; - } - } - } - - // 8. 应用颜色一致性优化 - if (fusedPixels > 0) { - DEBUG_EXTRA("应用颜色一致性优化"); - ApplyColorConsistencyOptimization(textureAtlas, pixelSamples, textureSize, colEmpty); - } + // 6. 应用梯度域优化 + DEBUG_EXTRA("应用梯度域优化..."); + ApplyGradientDomainOptimization(textureAtlas, colEmpty); - // 9. 填充空白区域 - // FillTextureGaps(textureAtlas, colEmpty); + // 7. 应用纹理接缝平滑 + DEBUG_EXTRA("应用纹理接缝平滑..."); + ApplyTextureSeamBlending(textureAtlas, colEmpty); - // 10. 应用轻微的颜色校正 - if (fusedPixels > 0) { - ApplyGlobalColorCorrection(textureAtlas, colEmpty, 0.3f); - } + // 8. 填充空白区域 + DEBUG_EXTRA("填充空白区域..."); + FillTextureGaps2(textureAtlas, colEmpty, 5); - // 11. 锐化处理 - if (fSharpnessWeight > 0 && fusedPixels > 0) { + // 9. 应用锐化 + if (fSharpnessWeight > 0) { + DEBUG_EXTRA("应用锐化: 强度=%.2f", fSharpnessWeight); ApplySoftSharpening(textureAtlas, fSharpnessWeight, colEmpty); } - // 12. 最终统计 + // 10. 最终统计 int validPixels = 0; for (int y = 0; y < textureSize; ++y) { for (int x = 0; x < textureSize; ++x) { @@ -14529,9 +14590,10 @@ Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasWith3DBridge( } } - DEBUG_EXTRA("纹理图集生成完成: 有效像素 %d / %d (%.2f%%)", - validPixels, textureSize * textureSize, - (float)validPixels * 100 / (textureSize * textureSize)); + float coverage = (float)validPixels * 100 / (textureSize * textureSize); + DEBUG_EXTRA("纹理生成完成: 覆盖率=%.2f%%, 有效像素=%d/%d", + coverage, validPixels, textureSize * textureSize); + DEBUG_EXTRA("总耗时: %s", TD_TIMER_GET_FMT().c_str()); return textures; } @@ -15489,32 +15551,179 @@ bool MeshTexture::PointInTriangle(const Point2f& p, const Point2f& a, const Poin return false; } -// 辅助函数:计算最佳纹理尺寸 -int MeshTexture::ComputeOptimalTextureSize(float uvWidth, float uvHeight, unsigned multiple) +// 计算最优纹理尺寸 +int MeshTexture::ComputeOptimalTextureSize(float uvWidth, float uvHeight, unsigned multiple) +{ + int baseSize = (int)(std::max(uvWidth, uvHeight) * 1024.0f); + int textureSize = ((baseSize + multiple - 1) / multiple) * multiple; + textureSize = std::max(256, std::min(4096, textureSize)); // 限制在合理范围 + return textureSize; +} + +// 多视图颜色优化 +void MeshTexture::ApplyMultiViewColorOptimization(Image8U3& texture, + const std::vector& faceSamplesData, + const Pixel8U& colEmpty) { - // 计算所需尺寸 - int baseWidth = (int)ceil(uvWidth * 2048); // 假设基础分辨率 - int baseHeight = (int)ceil(uvHeight * 2048); + DEBUG_EXTRA("多视图颜色优化"); - // 向上对齐到multiple的倍数 - int width = ((baseWidth + multiple - 1) / multiple) * multiple; - int height = ((baseHeight + multiple - 1) / multiple) * multiple; + Image8U3 optimized = texture.clone(); + int radius = 2; // 优化半径 - // 确保最小尺寸 - width = std::max(width, 256); - height = std::max(height, 256); + for (int y = radius; y < texture.rows - radius; ++y) { + for (int x = radius; x < texture.cols - radius; ++x) { + if (texture(y, x) == colEmpty) continue; + + cv::Vec3f currentColor(texture(y, x).b, texture(y, x).g, texture(y, x).r); + cv::Vec3f neighborSum(0, 0, 0); + int neighborCount = 0; + + // 收集邻域颜色 + 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) { + if (texture(ny, nx) != colEmpty) { + cv::Vec3f neighborColor(texture(ny, nx).b, texture(ny, nx).g, texture(ny, nx).r); + neighborSum += neighborColor; + neighborCount++; + } + } + } + } + + if (neighborCount > 0) { + cv::Vec3f neighborAvg = neighborSum / neighborCount; + cv::Vec3f diff = neighborAvg - currentColor; + float dist = std::sqrt(diff.dot(diff)); + + // 如果颜色差异不大,向邻域平均靠近 + if (dist < 30.0f) { + float blendFactor = 0.3f * (1.0f - dist / 30.0f); + cv::Vec3f newColor = currentColor * (1.0f - blendFactor) + neighborAvg * blendFactor; + + optimized(y, x) = Pixel8U( + (unsigned char)cv::saturate_cast(newColor[2]), + (unsigned char)cv::saturate_cast(newColor[1]), + (unsigned char)cv::saturate_cast(newColor[0]) + ); + } + } + } + } - // 使用最大尺寸 - int size = std::max(width, height); + optimized.copyTo(texture); +} + +// 填充纹理空隙 +void MeshTexture::FillTextureGaps2(Image8U3& texture, const Pixel8U& colEmpty, int patchSize) +{ + DEBUG_EXTRA("填充纹理空隙"); - // 限制最大尺寸 - const int MAX_SIZE = 8192; - if (size > MAX_SIZE) { - DEBUG_EXTRA("Warning: Texture size %d exceeds maximum %d, reducing...", size, MAX_SIZE); - size = std::min(size, MAX_SIZE); + cv::Mat mask = cv::Mat::zeros(texture.size(), CV_8U); + 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; + } + } } - return size; + // 使用邻域填充 + Image8U3 filled = texture.clone(); + for (int iter = 0; iter < 3; ++iter) { + for (int y = 0; y < texture.rows; ++y) { + for (int x = 0; x < texture.cols; ++x) { + if (texture(y, x) == colEmpty) { + cv::Vec3f sumColor(0, 0, 0); + int count = 0; + + for (int dy = -patchSize; dy <= patchSize; ++dy) { + for (int dx = -patchSize; dx <= patchSize; ++dx) { + int nx = x + dx; + int ny = y + dy; + + if (nx >= 0 && nx < texture.cols && ny >= 0 && ny < texture.rows) { + if (texture(ny, nx) != colEmpty) { + Pixel8U pixel = texture(ny, nx); + sumColor += cv::Vec3f(pixel.b, pixel.g, pixel.r); + count++; + } + } + } + } + + if (count > 0) { + cv::Vec3f avgColor = sumColor / count; + filled(y, x) = Pixel8U( + (unsigned char)cv::saturate_cast(avgColor[2]), + (unsigned char)cv::saturate_cast(avgColor[1]), + (unsigned char)cv::saturate_cast(avgColor[0]) + ); + } + } + } + } + + texture = filled.clone(); + } +} + +// 如果 Image8U3 是 OpenMVS 的图像类型 +void MeshTexture::ApplyTextureSeamBlending(Image8U3& texture, const Pixel8U& colEmpty) +{ + DEBUG_EXTRA("纹理接缝平滑"); + + cv::Mat gray; + cv::cvtColor(texture, gray, cv::COLOR_BGR2GRAY); + + // 创建掩码 + cv::Mat mask(texture.rows, texture.cols, CV_8UC1, cv::Scalar(0)); + + // 使用 OpenMVS 的原生访问方式 + for (int y = 0; y < texture.rows; ++y) { + const Pixel8U* rowPtr = texture.ptr(y); + uchar* maskRowPtr = mask.ptr(y); + + for (int x = 0; x < texture.cols; ++x) { + const Pixel8U& pixel = rowPtr[x]; + if (pixel != colEmpty) { + maskRowPtr[x] = 255; + } + } + } + + // 边缘检测 + cv::Mat edges; + cv::Canny(gray, edges, 50, 150); + + // 对边缘区域进行高斯模糊 + cv::Mat blurred; + cv::GaussianBlur(texture, blurred, cv::Size(3, 3), 0.8); + + // 混合原始图像和模糊图像 + for (int y = 0; y < texture.rows; ++y) { + Pixel8U* texRowPtr = texture.ptr(y); + const Pixel8U* blurRowPtr = blurred.ptr(y); + const uchar* maskRowPtr = mask.ptr(y); + const uchar* edgeRowPtr = edges.ptr(y); + + for (int x = 0; x < texture.cols; ++x) { + if (maskRowPtr[x] > 0 && edgeRowPtr[x] > 0) { + float alpha = 0.5f; // 边缘混合系数 + + Pixel8U& texPixel = texRowPtr[x]; + const Pixel8U& blurPixel = blurRowPtr[x]; + + for (int c = 0; c < 3; ++c) { + float value = texPixel[c] * (1.0f - alpha) + blurPixel[c] * alpha; + texPixel[c] = cv::saturate_cast(value); + } + } + } + } } // 保存遮挡数据到文件