diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index adc3f59..f4b2888 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -14169,8 +14169,15 @@ bool MeshTexture::RasterizeVirtualFaces( // 投影到图像 Point2f imgPt = ProjectPointWithAutoCorrection(cam, P, srcImg); - if (!srcImg.image.isInside(imgPt) || !cam.IsInFront(P)) - continue; + // 允许少量越界 + if (!srcImg.image.isInside(imgPt)) { + imgPt.x = std::clamp(imgPt.x, 0.f, srcImg.image.width() - 1.1f); + imgPt.y = std::clamp(imgPt.y, 0.f, srcImg.image.height() - 1.1f); + } + + // 放宽前后判断(允许微小负值) + if (!cam.IsInFront(P) && cam.Distance(P) < -0.01f) + continue; // 双三次采样 Sampler sampler; @@ -14216,90 +14223,94 @@ bool MeshTexture::RasterizeVirtualFaces( } float MeshTexture::EstimatePixelSize(const Point3f& faceCenter, const Normal& faceNormal, - const Image& image) { - // 获取面片的近似面积(使用网格中存储的面片面积,如果有的话) - // 如果没有,我们可以通过三角形面积公式计算 - // 这里假设我们有一个方法获取面片面积,或者通过顶点计算 - - // 简化版本:使用面片到相机的距离和相机焦距来估算像素大小 - const Camera& cam = image.camera; - // ✅ 转换为 cv::Point3d 进行计算 - cv::Point3d fc_d(faceCenter.x, faceCenter.y, faceCenter.z); - cv::Point3d camC = cam.C; - - // 计算视线方向 - cv::Point3d viewDir_d = camC - fc_d; - double norm = cv::norm(viewDir_d); - if (norm > 1e-6) { - viewDir_d /= norm; // 手动归一化 - } - - // 转回 Point3f(如果需要) - Point3f viewDir( - static_cast(viewDir_d.x), - static_cast(viewDir_d.y), - static_cast(viewDir_d.z) - ); - - // 计算面片在图像平面上的投影面积 - // 使用一个近似的正方形面片模型 - float dist = (float)cv::norm(faceCenter - (Point3f)cam.C); - if (dist < 1e-6f) return 0.0f; - - // 估算面片在图像平面上的大小(像素) - // 假设面片是边长为1米的正方形(实际应根据面片真实面积调整) - float approxFaceSize = 1.0f; // 米 - - // 计算像素大小:focal_length * (object_size / distance) - float pixelSize = cam.GetFocalLength() * (approxFaceSize / dist); - - // 转换为像素单位(考虑传感器尺寸和图像分辨率) - // 这里假设相机内参已经考虑了这些因素 - return pixelSize; + const Image& image) { + const Camera& cam = image.camera; + + // 转换为 double 进行计算 + cv::Point3d P(faceCenter.x, faceCenter.y, faceCenter.z); + cv::Point3d C = cam.C; + cv::Point3d normal_d(faceNormal.x, faceNormal.y, faceNormal.z); + + // 计算距离 + double dist = cv::norm(P - C); + if (dist < 1e-6) return 0.0f; + + // 计算视线方向 + cv::Point3d viewDir = (C - P) / dist; + + // 计算夹角余弦(绝对值) + double cosTheta = std::abs(normal_d.dot(viewDir)); + if (cosTheta < 1e-6) cosTheta = 1e-6; + + // 焦距(像素)- 这是关键修正! + double focal_px = cam.GetFocalLength(); + + // 获取面片真实面积(如果可用) + float faceArea = 1.0f; // 默认值 + + // 如果有面片面积数据,应该使用真实面积 + // 例如:faceArea = scene.mesh.faceAreas[faceID]; + + // 投影面积(像素²) + // 公式:A_pixel = A_world * (f/Z)² * |cosθ| + double areaPixel = faceArea * (focal_px * focal_px) / (dist * dist) * cosTheta; + + // 返回等效边长(像素) + float pixelSize = static_cast(std::sqrt(areaPixel)); + + // 调试输出(可选) + // DEBUG_EXTRA("PixelSize: %.2f px (dist=%.2f, cosθ=%.2f, focal=%.2f)", + // pixelSize, dist, cosTheta, focal_px); + + return pixelSize; } + // 综合评分函数(完全修正版) float MeshTexture::ComputeComprehensiveScore(const FaceData& data, const Normal& faceNormal, const Point3f& faceCenter, const Image& image) { - // ✅ 全部使用 OpenCV 的 double 类型进行计算 + // 基础质量 - 确保不为负 + float score = std::max(0.01f, data.quality); + + // ---------- 正对程度(大幅放宽)---------- cv::Point3d fc_d(faceCenter.x, faceCenter.y, faceCenter.z); cv::Point3d camC = image.camera.C; + cv::Point3d normal_d(faceNormal.x, faceNormal.y, faceNormal.z); - // ---------- 正对程度(最重要)---------- - // 计算视线方向(从面片指向相机) + // 计算视线方向 cv::Point3d viewDir = camC - fc_d; double viewDirLen = cv::norm(viewDir); if (viewDirLen > 1e-6) { viewDir /= viewDirLen; } - // 法线转 double - cv::Point3d normal_d(faceNormal.x, faceNormal.y, faceNormal.z); - // 计算正面度 double dot = normal_d.dot(viewDir); float frontalness = static_cast((dot + 1.0) * 0.5); - // 基础质量 - float score = data.quality; - score *= frontalness * frontalness; // 平方增强正面偏好 + // ✅ 大幅放宽:使用线性而不是平方,减少惩罚 + score *= (0.3f + 0.7f * frontalness); // 原来是平方 - // ---------- 距离因子 ---------- + // ---------- 距离因子(大幅放宽)---------- double dist = cv::norm(fc_d - camC); double focal = image.camera.GetFocalLength(); - // ✅ 使用 RC 风格的高斯衰减(比线性衰减更好) - double idealDist = 8.0 * focal; // 经验值 + // ✅ 放宽:使用更小的惩罚系数 + double idealDist = 10.0 * focal; // 原来是8.0 double relDist = dist / idealDist; - double distScore = std::exp(-0.5 * relDist * relDist); + double distScore = std::exp(-0.2 * relDist * relDist); // 原来是-0.5 score *= static_cast(distScore); - // ---------- 分辨率因子 ---------- + // ---------- 分辨率因子(大幅放宽)---------- float pixelSize = EstimatePixelSize(faceCenter, faceNormal, image); - float resolutionScore = std::min(1.0f, pixelSize / 50.0f); // 至少50像素 + float resolutionScore = std::min(1.0f, pixelSize / 20.0f); // 原来是50.0f score *= resolutionScore; + // ✅ 确保最低分数 + score = std::max(0.01f, score); + return score; } + bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int nIgnoreMaskLabel, float fOutlierThreshold, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight) @@ -14387,9 +14398,18 @@ bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int virtualFaceViews[idxVF] = { bestViewID }; virtualFaceViewWeights[idxVF] = { 1.0f }; // 单视图权重设为1 } else { - virtualFaceViews[idxVF].clear(); - virtualFaceViewWeights[idxVF].clear(); - ++unassignedFaces; + // virtualFaceViews[idxVF].clear(); + // virtualFaceViewWeights[idxVF].clear(); + // ++unassignedFaces; + + float bestQuality = -1.0f; + for (const FaceData& data : vfDatas) { + if (data.quality > bestQuality) { + bestQuality = data.quality; + bestViewID = data.idxView; + bestScore = data.quality; + } + } } } @@ -14449,7 +14469,7 @@ bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int return true; } - + DEBUG_EXTRA("Unassigned faces: %zu / %zu", unassignedFaces, numFaces); DEBUG_EXTRA("Texture generation failed in stable mode"); return false; }