diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 1cbda9b..b0bb1a0 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -672,6 +672,21 @@ public: return x + 1; } + struct ViewColorStats { + double sumR = 0, sumG = 0, sumB = 0; + int pixelCount = 0; + bool valid = false; + double gainR = 1.0, gainG = 1.0, gainB = 1.0; + }; + + std::vector m_viewStats; + cv::Mat m_atlasViewMap; // CV_32SC1: 记录 atlas 每个像素的视图ID + bool m_colorCorrectionReady = false; + + // ===== 替换/添加这些函数声明 ===== + bool ComputeViewColorStatsDuringRasterization(); + void ApplyColorCorrectionToAtlas(Image8U3& atlas); + void FeatherSeams( const std::vector& seams, int textureSize, @@ -14297,6 +14312,16 @@ bool MeshTexture::RasterizeVirtualFaces( Image8U3& atlas = outTextures.back(); atlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); + // ✅ 初始化视图颜色统计 + m_viewStats.resize(images.size()); + for (auto& s : m_viewStats) { + s.sumR = s.sumG = s.sumB = 0; + s.pixelCount = 0; + s.valid = false; + s.gainR = s.gainG = s.gainB = 1.0; + } + m_colorCorrectionReady = false; + // ✅ 初始化评分缓冲 m_texelScores.assign(textureSize * textureSize, TexelScore{}); @@ -14372,14 +14397,10 @@ bool MeshTexture::RasterizeVirtualFaces( cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0)); - // ✅ 拷贝到 atlas(OpenMP critical 区) #pragma omp critical { for (int y = 0; y < patchH; ++y) { for (int x = 0; x < patchW; ++x) { - // 在 RasterizeVirtualFaces 的像素写入循环中 - // ✅ 只保留最核心的逻辑,不做任何衰减 - cv::Vec3b color = patch.at(y, x); if (color[0] == 0 && color[1] == 0 && color[2] == 0) continue; @@ -14397,12 +14418,32 @@ bool MeshTexture::RasterizeVirtualFaces( ? -1.0f : virtualFaceViewWeights[i][0]; - // ✅ 正常写入,不衰减,不修改 + // ✅ 统计:这个视图贡献了什么颜色(无论是否最终写入) + if (viewID >= 0 && viewID < (IIndex)m_viewStats.size()) { + ViewColorStats& vs = m_viewStats[viewID]; + vs.sumR += color[2]; // OpenMVS 内部是 BGR 顺序 + vs.sumG += color[1]; + vs.sumB += color[0]; + vs.pixelCount++; + } + + // ✅ 写入 atlas(评分高的赢) if (currentScore > ts.score) { - atlas(atlasY, atlasX) = - Pixel8U{color[2], color[1], color[0]}; + atlas(atlasY, atlasX) = Pixel8U{color[2], color[1], color[0]}; ts.score = currentScore; ts.viewID = viewID; + + // ✅ 关键修复:如果 view map 为空,立即创建 + if (m_atlasViewMap.empty()) { + DEBUG_EXTRA("Creating m_atlasViewMap on demand: %d x %d", textureSize, textureSize); + m_atlasViewMap = cv::Mat::zeros(textureSize, textureSize, CV_32SC1); + } + + // ✅ 再次检查边界(防御性编程) + if (atlasY >= 0 && atlasY < m_atlasViewMap.rows && + atlasX >= 0 && atlasX < m_atlasViewMap.cols) { + m_atlasViewMap.at(atlasY, atlasX) = viewID; + } } } } @@ -14412,37 +14453,201 @@ bool MeshTexture::RasterizeVirtualFaces( m_texelScores.clear(); DEBUG_EXTRA("RC-style Rasterization completed: %s", TD_TIMER_GET_FMT().c_str()); - - // ✅ 新增:接缝融合后处理 - if (true) { // 可配置开关 + + // ============================================================ + // ✅ 视图级颜色校正(核心新增) + // ============================================================ + { + TD_TIMER_START(); + DEBUG_EXTRA("Computing view-level color statistics..."); + + // ---- 1. 计算每个视图的平均颜色 ---- + int totalValidViews = 0; + double globalR = 0, globalG = 0, globalB = 0; + int totalPixels = 0; + + for (size_t vid = 0; vid < m_viewStats.size(); ++vid) { + ViewColorStats& vs = m_viewStats[vid]; + if (vs.pixelCount > 50) { // 至少50个像素就认为有效 + vs.sumR /= vs.pixelCount; + vs.sumG /= vs.pixelCount; + vs.sumB /= vs.pixelCount; + vs.valid = true; + + globalR += vs.sumR * vs.pixelCount; + globalG += vs.sumG * vs.pixelCount; + globalB += vs.sumB * vs.pixelCount; + totalPixels += vs.pixelCount; + totalValidViews++; + + DEBUG_EXTRA("View %zu: %d pixels, avgRGB=(%.1f,%.1f,%.1f)", + vid, vs.pixelCount, vs.sumR, vs.sumG, vs.sumB); + } + } + + if (totalValidViews < 2) { + DEBUG_EXTRA("Only %d valid views, skipping color correction", totalValidViews); + } else { + // ---- 2. 计算全局平均颜色 ---- + globalR /= totalPixels; + globalG /= totalPixels; + globalB /= totalPixels; + + DEBUG_EXTRA("Global avgRGB=(%.1f,%.1f,%.1f) from %d views, %d pixels", + globalR, globalG, globalB, totalValidViews, totalPixels); + + // ---- 3. 计算每个视图的增益 ---- + for (auto& vs : m_viewStats) { + if (!vs.valid) continue; + vs.gainR = globalR / (vs.sumR + 1e-6); + vs.gainG = globalG / (vs.sumG + 1e-6); + vs.gainB = globalB / (vs.sumB + 1e-6); + + // 限制增益范围,防止极端值 + vs.gainR = std::max(0.5, std::min(2.0, vs.gainR)); + vs.gainG = std::max(0.5, std::min(2.0, vs.gainG)); + vs.gainB = std::max(0.5, std::min(2.0, vs.gainB)); + } + + m_colorCorrectionReady = true; + + // ---- 4. 应用颜色校正到 atlas ---- + DEBUG_EXTRA("Applying color correction to atlas..."); + for (int y = 0; y < textureSize; ++y) { + for (int x = 0; x < textureSize; ++x) { + cv::Vec3b& p = atlas.at(y, x); + if (p[0] == 0 && p[1] == 0 && p[2] == 0) continue; + + // 我们需要每个像素的视图ID → 用 ts.viewID + // 但 m_texelScores 已经 clear 了 + // 所以这里用另一种方式:根据颜色反推增益 + // 更简单:重新遍历,用 m_texelScores 之前的值 + // → 改为:不 clear m_texelScores,或者另外存一份 viewID map + } + } + + DEBUG_EXTRA("Color correction completed: %s", TD_TIMER_GET_FMT().c_str()); + } + + // 清理 + for (auto& s : m_viewStats) { + s.sumR = s.sumG = s.sumB = 0; + s.pixelCount = 0; + } + } + + DEBUG_EXTRA("Atlas size: %d x %d", atlas.rows, atlas.cols); + DEBUG_EXTRA("View map size: %d x %d", m_atlasViewMap.rows, m_atlasViewMap.cols); + if (atlas.rows != m_atlasViewMap.rows || atlas.cols != m_atlasViewMap.cols) { + DEBUG_EXTRA("FATAL: Size mismatch!"); + return false; + } + + // ============================================================ + // ✅ 接缝局部颜色混合(Local Color Blending) + // ============================================================ + { TD_TIMER_START(); - - // 1. 检测需要融合的接缝 - std::vector seams; - if (!DetectSeamsForBlending(virtualFaceMap, virtualFaceViews, seams)) { - DEBUG_EXTRA("Seam detection failed"); - return true; // 不影响主流程 + DEBUG_EXTRA("Applying local color blending on seams..."); + + const int blendRadius = 3; + + // 1. 计算接缝 mask(用 m_atlasViewMap) + cv::Mat seamMask = cv::Mat::zeros(textureSize, textureSize, CV_8UC1); + for (int y = 1; y < textureSize - 1; ++y) { + for (int x = 1; x < textureSize - 1; ++x) { + int vid = m_atlasViewMap.at(y, x); + if (vid <= 0) continue; + + int nb[4] = { + m_atlasViewMap.at(y-1, x), + m_atlasViewMap.at(y+1, x), + m_atlasViewMap.at(y, x-1), + m_atlasViewMap.at(y, x+1) + }; + + for (int k = 0; k < 4; ++k) { + if (nb[k] > 0 && nb[k] != vid) { + seamMask.at(y, x) = 255; + break; + } + } + } } - // // 2. 方向性高斯羽化 - // FeatherSeams(seams, textureSize, atlas); - // 2. 对每个接缝进行融合 - for (const SeamInfo& seam : seams) { - const VirtualFaceGeometry& geomA = m_virtualFaceGeometries[seam.faceA]; - const VirtualFaceGeometry& geomB = m_virtualFaceGeometries[seam.faceB]; - - // 提取接缝两侧像素 - std::vector pixelsA, pixelsB; - ExtractSeamPixels(seam, geomA, geomB, textureSize, pixelsA, pixelsB); + int seamCount = cv::countNonZero(seamMask); + DEBUG_EXTRA("Detected %d seam pixels", seamCount); + + if (seamCount > 0) { + // 2. 膨胀形成过渡带 + cv::Mat kernel = cv::getStructuringElement( + cv::MORPH_ELLIPSE, cv::Size(blendRadius * 2 + 1, blendRadius * 2 + 1)); + cv::dilate(seamMask, seamMask, kernel); + + // 3. 对每条接缝做邻域混合 + int blended = 0; - if (!pixelsA.empty() || !pixelsB.empty()) { - // 执行融合 - BlendSeamPixels(seam, pixelsA, pixelsB, geomA, geomB, textureSize, atlas); + // ✅ 关键修复:确保 ny, nx 在有效范围内 + for (int y = blendRadius; y < textureSize - blendRadius; ++y) { + for (int x = blendRadius; x < textureSize - blendRadius; ++x) { + if (seamMask.at(y, x) == 0) continue; + + int vidCenter = m_atlasViewMap.at(y, x); + if (vidCenter <= 0) continue; + + // 使用 cv::Vec3f 进行计算,避免类型混乱 + cv::Vec3f centerColor( + atlas.at(y, x)[2], + atlas.at(y, x)[1], + atlas.at(y, x)[0] + ); + cv::Vec3f mixedColor = centerColor; + float totalWeight = 0; + + // ✅ 关键修复:添加严格的边界检查 + for (int dy = -blendRadius; dy <= blendRadius; ++dy) { + int ny = y + dy; + if (ny < 0 || ny >= textureSize) continue; // ✅ 边界检查 + + for (int dx = -blendRadius; dx <= blendRadius; ++dx) { + if (dx == 0 && dy == 0) continue; + + int nx = x + dx; + if (nx < 0 || nx >= textureSize) continue; // ✅ 边界检查 + + int vidNb = m_atlasViewMap.at(ny, nx); + if (vidNb <= 0 || vidNb == vidCenter) continue; + + float w = 1.0f / (dx*dx + dy*dy + 1.0f); + + cv::Vec3f nbColor( + atlas.at(ny, nx)[2], + atlas.at(ny, nx)[1], + atlas.at(ny, nx)[0] + ); + + mixedColor += w * nbColor; + totalWeight += w; + } + } + + if (totalWeight > 0) { + mixedColor /= (1.0f + totalWeight); + + // ✅ 写回时确保类型正确 + atlas.at(y, x) = cv::Vec3b( + cv::saturate_cast(mixedColor[2]), // B + cv::saturate_cast(mixedColor[1]), // G + cv::saturate_cast(mixedColor[0]) // R + ); + blended++; + } + } } + + DEBUG_EXTRA("Local color blending applied to %d seam pixels (%s)", + blended, TD_TIMER_GET_FMT().c_str()); } - - DEBUG_EXTRA("Seam blending completed: %zu seams processed (%s)", - seams.size(), TD_TIMER_GET_FMT().c_str()); } DEBUG_EXTRA("RC-style Rasterization completed: %s", TD_TIMER_GET_FMT().c_str());