diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 5ee9755..4118166 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -827,50 +827,6 @@ public: unsigned nTextureSizeMultiple, Pixel8U colEmpty, Mesh::Image8U3Arr& outTextures); - - inline float ComputeLocalVariance(const cv::Mat& image, int patchW, int patchH) { - cv::Mat gray; - if (image.channels() == 3) { - cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY); - } else { - gray = image.clone(); - } - - // 1. 局部方差 - cv::Mat mean, sqrMean; - cv::boxFilter(gray, mean, CV_32F, cv::Size(5, 5)); - cv::boxFilter(gray.mul(gray), sqrMean, CV_32F, cv::Size(5, 5)); - cv::Mat variance = sqrMean - mean.mul(mean); - float varScore = cv::mean(variance)[0] / 0.08f; - - // 2. 梯度幅值 - cv::Mat gradX, gradY; - cv::Sobel(gray, gradX, CV_32F, 1, 0, 3); - cv::Sobel(gray, gradY, CV_32F, 0, 1, 3); - cv::Mat magnitude; - cv::magnitude(gradX, gradY, magnitude); - float gradScore = cv::mean(magnitude)[0] * 3.0f; - - // 3. 直方图熵(可选,衡量纹理丰富度) - int histSize = 256; - float range[] = {0, 256}; - const float* histRange = {range}; - cv::Mat hist; - cv::calcHist(&gray, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange); - hist /= gray.total(); - - float entropy = 0; - for (int i = 0; i < histSize; i++) { - float p = hist.at(i); - if (p > 0) entropy -= p * log2f(p); - } - float entropyScore = entropy / 8.0f; // 8 bits 的最大熵 - - // 综合评分(可调整权重) - float complexity = 0.4f * varScore + 0.4f * gradScore + 0.2f * entropyScore; - - return std::min(complexity, 1.0f); - } inline bool ComputeBarycentric(float u, float v, const TexCoord& uv0, const TexCoord& uv1, const TexCoord& uv2, @@ -16708,8 +16664,8 @@ bool MeshTexture::RasterizeVirtualFaces( mainPatch.convertTo(mainFloat, CV_32FC3, 1.0f / 255.0f); cv::Mat mainLowFreq; - cv::GaussianBlur(mainFloat, mainLowFreq, cv::Size(5, 5), 1.5); - cv::Mat highFreqAccum = (mainFloat - mainLowFreq) * 0.9f; + cv::GaussianBlur(mainFloat, mainLowFreq, cv::Size(31, 31), 10.0); + cv::Mat highFreqAccum = (mainFloat - mainLowFreq) * 0.6f; float highFreqTotalWeight = 0.6f; if (hasLowFreqViews) { @@ -16739,83 +16695,83 @@ bool MeshTexture::RasterizeVirtualFaces( cv::Mat highFreq = highFreqAccum; - // ========== 每个顶点独立判断可见性(增强版)========== - bool vertexVisible[3] = {false, false, false}; - cv::Vec3f vertexBestColor[3] = {cv::Vec3f(0,0,0), cv::Vec3f(0,0,0), cv::Vec3f(0,0,0)}; +// ========== 每个顶点独立判断可见性(增强版)========== +bool vertexVisible[3] = {false, false, false}; +cv::Vec3f vertexBestColor[3] = {cv::Vec3f(0,0,0), cv::Vec3f(0,0,0), cv::Vec3f(0,0,0)}; - // 检查主视图 - for (int j = 0; j < 3; ++j) { - int vx = (int)(uv[j].x * textureSize) - minX; - int vy = (int)(uv[j].y * textureSize) - minY; - if (vx >= 0 && vx < patchW && vy >= 0 && vy < patchH) { - cv::Vec3f color = mainFloat.at(vy, vx); - if (color[0] > 0.0f || color[1] > 0.0f || color[2] > 0.0f) { - vertexVisible[j] = true; - vertexBestColor[j] = color; - } - } - } +// 检查主视图 +for (int j = 0; j < 3; ++j) { + int vx = (int)(uv[j].x * textureSize) - minX; + int vy = (int)(uv[j].y * textureSize) - minY; + if (vx >= 0 && vx < patchW && vy >= 0 && vy < patchH) { + cv::Vec3f color = mainFloat.at(vy, vx); + if (color[0] > 0.0f || color[1] > 0.0f || color[2] > 0.0f) { + vertexVisible[j] = true; + vertexBestColor[j] = color; + } + } +} - // 如果主视图不可见,检查辅助视图 - if (hasLowFreqViews && (!vertexVisible[0] || !vertexVisible[1] || !vertexVisible[2])) { - for (const auto& lfv : lowFreqViews) { - if (lfv.viewId >= (IIndex)images.size() || images[lfv.viewId].image.empty()) continue; - - // 对该辅助视图重新采样该顶点位置 - const Camera& auxCam = images[lfv.viewId].camera; - bool auxCamValid = (auxCam.K(0,0) > 0 && auxCam.K(1,1) > 0); - - for (int j = 0; j < 3; ++j) { - if (vertexVisible[j]) continue; // 已经可见就跳过 - - // 用3D投影得到该顶点在辅助视图中的像素位置 - Point2f imgPt; - if (auxCamValid) { - imgPt = auxCam.ProjectPointP(v[j]); - } else { - // 用 homography fallback - const auto& geomRef = m_virtualFaceGeometries[i]; - auto it = geomRef.viewHomographies.find(lfv.viewId); - if (it == geomRef.viewHomographies.end()) continue; - - const float* H = it->second.ptr(); - float u = uv[j].x; - float v_coord = uv[j].y; - float w = H[6]*u + H[7]*v_coord + H[8]; - if (std::abs(w) < 1e-12f) continue; - - float projX = (H[0]*u + H[1]*v_coord + H[2]) / w; - float projY = (H[3]*u + H[4]*v_coord + H[5]) / w; - imgPt = Point2f(projX, projY); - } - - if (imgPt.x >= 0 && imgPt.x < images[lfv.viewId].image.cols && - imgPt.y >= 0 && imgPt.y < images[lfv.viewId].image.rows) { - // 采样该像素颜色 - cv::Vec3f auxColor = imagesFloat[lfv.viewId].at((int)imgPt.y, (int)imgPt.x); - if (auxColor[0] > 0.0f || auxColor[1] > 0.0f || auxColor[2] > 0.0f) { - vertexVisible[j] = true; - vertexBestColor[j] = auxColor; - } - } - } - - // 如果三个顶点都已可见,提前退出 - if (vertexVisible[0] && vertexVisible[1] && vertexVisible[2]) - break; - } - } +// 如果主视图不可见,检查辅助视图 +if (hasLowFreqViews && (!vertexVisible[0] || !vertexVisible[1] || !vertexVisible[2])) { + for (const auto& lfv : lowFreqViews) { + if (lfv.viewId >= (IIndex)images.size() || images[lfv.viewId].image.empty()) continue; + + // 对该辅助视图重新采样该顶点位置 + const Camera& auxCam = images[lfv.viewId].camera; + bool auxCamValid = (auxCam.K(0,0) > 0 && auxCam.K(1,1) > 0); + + for (int j = 0; j < 3; ++j) { + if (vertexVisible[j]) continue; // 已经可见就跳过 + + // 用3D投影得到该顶点在辅助视图中的像素位置 + Point2f imgPt; + if (auxCamValid) { + imgPt = auxCam.ProjectPointP(v[j]); + } else { + // 用 homography fallback + const auto& geomRef = m_virtualFaceGeometries[i]; + auto it = geomRef.viewHomographies.find(lfv.viewId); + if (it == geomRef.viewHomographies.end()) continue; + + const float* H = it->second.ptr(); + float u = uv[j].x; + float v_coord = uv[j].y; + float w = H[6]*u + H[7]*v_coord + H[8]; + if (std::abs(w) < 1e-12f) continue; + + float projX = (H[0]*u + H[1]*v_coord + H[2]) / w; + float projY = (H[3]*u + H[4]*v_coord + H[5]) / w; + imgPt = Point2f(projX, projY); + } + + if (imgPt.x >= 0 && imgPt.x < images[lfv.viewId].image.cols && + imgPt.y >= 0 && imgPt.y < images[lfv.viewId].image.rows) { + // 采样该像素颜色 + cv::Vec3f auxColor = imagesFloat[lfv.viewId].at((int)imgPt.y, (int)imgPt.x); + if (auxColor[0] > 0.0f || auxColor[1] > 0.0f || auxColor[2] > 0.0f) { + vertexVisible[j] = true; + vertexBestColor[j] = auxColor; + } + } + } + + // 如果三个顶点都已可见,提前退出 + if (vertexVisible[0] && vertexVisible[1] && vertexVisible[2]) + break; + } +} - #pragma omp critical (vertex_visibility) - { - for (int j = 0; j < 3; ++j) { - VIndex vi = face[j]; - if (vi < m_vertexColors.size() && vertexVisible[j]) { - m_vertexColors[vi].visibleInAnyView = true; - m_vertexColors[vi].mainColor = vertexBestColor[j]; - } - } - } +#pragma omp critical (vertex_visibility) +{ + for (int j = 0; j < 3; ++j) { + VIndex vi = face[j]; + if (vi < m_vertexColors.size() && vertexVisible[j]) { + m_vertexColors[vi].visibleInAnyView = true; + m_vertexColors[vi].mainColor = vertexBestColor[j]; + } + } +} // ========== 空洞填充与合成 ========== cv::Mat filledLowFreq = lowFreqPatch.clone(); @@ -16828,10 +16784,8 @@ bool MeshTexture::RasterizeVirtualFaces( } } - // 使用自适应权重,根据纹理复杂度决定 - float textureComplexity = ComputeLocalVariance(mainFloat, patchW, patchH); - float lowFreqWeight = 0.3f + 0.4f * (1.0f - textureComplexity); - cv::Mat finalPatch = filledLowFreq * lowFreqWeight + mainFloat * (1.0f - lowFreqWeight); + // cv::Mat finalPatch = filledLowFreq * 0.7f + mainFloat * 0.3f; + cv::Mat finalPatch = filledLowFreq; // 钳制到 [0, 1] cv::Mat clampedPatch; @@ -16863,8 +16817,7 @@ bool MeshTexture::RasterizeVirtualFaces( if (minDist < pad) { weight = minDist / (float)pad; - // 使用线性衰减而非平方衰减 - weight = std::min(minDist / (float)pad, 1.0f); // 原 weight * weight + weight = weight * weight; } if (currentScore > m_texelScores[idx].score) { @@ -16967,35 +16920,35 @@ bool MeshTexture::RasterizeVirtualFaces( continue; // 找到这个像素属于哪个面 - int patchID = m_texelPatchID[idx]; - if (patchID >= (int)virtualFaceMap.size()) - continue; + int patchID = m_texelPatchID[idx]; +if (patchID >= (int)virtualFaceMap.size()) + continue; - if (patchID == NO_ID) { - // 尝试找最近的已填充像素 - int bestDist = INT_MAX; - int bestPatchID = NO_ID; - int searchRad = 16; - for (int dy = -searchRad; dy <= searchRad; ++dy) { - for (int dx = -searchRad; dx <= searchRad; ++dx) { - int sx = atlasX + dx, sy = atlasY + dy; - if (sx < 0 || sx >= textureSize || sy < 0 || sy >= textureSize) continue; - size_t sidx = sy * textureSize + sx; - if (m_texelPatchID[sidx] != NO_ID) { - int dist = dx*dx + dy*dy; - if (dist < bestDist) { - bestDist = dist; - bestPatchID = m_texelPatchID[sidx]; - } - } - } - } - if (bestPatchID != NO_ID) { - patchID = bestPatchID; - } else { - continue; // 实在找不到归属面,跳过 - } - } +if (patchID == NO_ID) { + // 尝试找最近的已填充像素 + int bestDist = INT_MAX; + int bestPatchID = NO_ID; + int searchRad = 16; + for (int dy = -searchRad; dy <= searchRad; ++dy) { + for (int dx = -searchRad; dx <= searchRad; ++dx) { + int sx = atlasX + dx, sy = atlasY + dy; + if (sx < 0 || sx >= textureSize || sy < 0 || sy >= textureSize) continue; + size_t sidx = sy * textureSize + sx; + if (m_texelPatchID[sidx] != NO_ID) { + int dist = dx*dx + dy*dy; + if (dist < bestDist) { + bestDist = dist; + bestPatchID = m_texelPatchID[sidx]; + } + } + } + } + if (bestPatchID != NO_ID) { + patchID = bestPatchID; + } else { + continue; // 实在找不到归属面,跳过 + } +} const VirtualFace& vf = virtualFaceMap[patchID]; if (vf.faces.empty()) continue; @@ -17041,7 +16994,7 @@ bool MeshTexture::RasterizeVirtualFaces( fillColor = vertexColors[bestVertex]; } - // ★★★ 新增:检查 fillColor 是否有效(接近黑色)★★★ +// ★★★ 新增:检查 fillColor 是否有效(接近黑色)★★★ bool fillIsBlack = (fillColor[0] < 0.05f && fillColor[1] < 0.05f && fillColor[2] < 0.05f); if (fillIsBlack) { // Fallback:从邻近非空洞像素采样 @@ -17157,11 +17110,73 @@ bool MeshTexture::RasterizeVirtualFaces( int validPixels = 0; for (size_t i = 0; i < m_texelPatchID.size(); ++i) if (m_texelPatchID[i] != NO_ID) validPixels++; - DEBUG_EXTRA("m_texelPatchID: %d valid pixels out of %zu", validPixels, m_texelPatchID.size()); + DEBUG_EXTRA("m_texelPatchID: %d valid pixels out of %zu", validPixels, m_texelPatchID.size()); + + // ★★★ 全局空洞终极填充(优化版)★★★ + cv::Mat holeMask(textureSize, textureSize, CV_8UC1); + for (int y = 0; y < textureSize; ++y) { + for (int x = 0; x < textureSize; ++x) { + Pixel8U px = atlas(y, x); + holeMask.at(y, x) = (px[0] == colEmpty.b && px[1] == colEmpty.g && px[2] == colEmpty.r) ? 255 : 0; + } + } + + // 多次膨胀,逐步向内填充 + cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(5, 5)); + cv::Mat dilatedMask; + cv::Mat prevMask = holeMask.clone(); + + for (int iter = 0; iter < 30; ++iter) { // 最多膨胀30次(约150像素半径) + cv::dilate(prevMask, dilatedMask, kernel); + + // 找出新覆盖的区域 + cv::Mat newPixels = dilatedMask & ~prevMask; + if (cv::countNonZero(newPixels) == 0) break; // 没有新像素了 + + // 对新像素赋值为最近的非空洞像素颜色 + for (int y = 0; y < textureSize; ++y) { + for (int x = 0; x < textureSize; ++x) { + if (newPixels.at(y, x)) { + // 在原始非空洞区域找最近的颜色 + int bestDist = INT_MAX; + Pixel8U bestColor(128, 128, 128); + int rad = 3; + for (int dy = -rad; dy <= rad && bestDist > 1; ++dy) { + for (int dx = -rad; dx <= rad && bestDist > 1; ++dx) { + int sx = x + dx, sy = y + dy; + if (sx < 0 || sx >= textureSize || sy < 0 || sy >= textureSize) continue; + if (!holeMask.at(sy, sx)) { // 原始非空洞像素 + Pixel8U spx = atlas(sy, sx); + int dist = dx*dx + dy*dy; + if (dist < bestDist) { + bestDist = dist; + bestColor = spx; + } + } + } + } + atlas(y, x) = bestColor; + } + } + } + + prevMask = dilatedMask; + } + + // 如果还有剩余空洞,用整体平均色填充 + cv::Scalar avgColor = cv::mean(atlas, holeMask == 0); + Pixel8U avgPx((uchar)avgColor[0], (uchar)avgColor[1], (uchar)avgColor[2]); + for (int y = 0; y < textureSize; ++y) { + for (int x = 0; x < textureSize; ++x) { + if (holeMask.at(y, x)) { + atlas(y, x) = avgPx; + } + } + } return true; } - + bool MeshTexture::BuildSeamVertsFromEdges() { const size_t NP = rcPatches.size();