Browse Source

加View Smoothing

ManualUV
hesuicong 4 weeks ago
parent
commit
864f5cafb9
  1. 155
      libs/MVS/SceneTexture.cpp

155
libs/MVS/SceneTexture.cpp

@ -648,6 +648,8 @@ public: @@ -648,6 +648,8 @@ public:
unsigned minCommonCameras, float fOutlierThreshold,
float fRatioDataSmoothness, int nIgnoreMaskLabel,
const IIndexArr& views);
// 简单的 view smoothing:如果某个面的 view 和多数邻居不同,且分数差距不大,就改成邻居的 view
float ComputeViewNormalScore(const Mesh::Normal& faceNormal, const Camera& camera, const Point3f& faceCenter);
float ComputeResolutionScore(
const Camera& camera,
@ -822,7 +824,44 @@ public: @@ -822,7 +824,44 @@ public:
IIndex viewID = NO_ID;
};
std::vector<TexelScore> m_texelScores;
std::vector<uint32_t> m_texelPatchID; // 每个 texel 属于哪个 rcPatch(-1 表示无)
inline void SmoothVirtualFaceViews(
std::vector<std::vector<IIndex>>& virtualFaceViews,
const Mesh::FaceFacesArr& faceFaces, // ← 改成这个类型
int iterations = 2)
{
for (int iter = 0; iter < iterations; ++iter) {
auto newViews = virtualFaceViews;
for (size_t i = 0; i < virtualFaceViews.size(); ++i) {
if (virtualFaceViews[i].empty()) continue;
IIndex myView = virtualFaceViews[i][0];
std::map<IIndex, int> vote;
int validNeighbors = 0;
// faceFaces[i] 是 TPoint3<FIndex>,用 .x .y .z 或 [0][1][2] 访问3个邻居
const auto& ff = faceFaces[i];
for (int k = 0; k < 3; ++k) {
FIndex nb = ff[k]; // ff.x / ff.y / ff.z 也行
if (nb < (FIndex)virtualFaceViews.size() && !virtualFaceViews[nb].empty()) {
vote[virtualFaceViews[nb][0]]++;
validNeighbors++;
}
}
if (validNeighbors < 2) continue;
auto best = std::max_element(vote.begin(), vote.end(),
[](const std::pair<IIndex, int>& a, const std::pair<IIndex, int>& b) {
return a.second < b.second;
});
if (best->first != myView && best->second >= 2) {
newViews[i][0] = best->first;
}
}
virtualFaceViews.swap(newViews);
}
}
float ComputeComprehensiveScore(const FaceData& data, const Normal& faceNormal,
const Point3f& faceCenter, const Image& image);
float EstimatePixelSize(const Point3f& faceCenter, const Normal& faceNormal,
@ -14390,7 +14429,7 @@ void MeshTexture::LocalSeamBlending(Image8U3& atlas, int textureSize) @@ -14390,7 +14429,7 @@ void MeshTexture::LocalSeamBlending(Image8U3& atlas, int textureSize)
cv::Rect overlap = rcPatches[e.rcPatchID0].rect & rcPatches[e.rcPatchID1].rect;
if (overlap.width <= 0 || overlap.height <= 0) continue;
// 只取 overlap 的边界环(带状),宽度 ~8px
int bw = 8;
int bw = 16;
cv::Rect inner(overlap.x + bw, overlap.y + bw,
std::max(0, overlap.width - 2*bw),
std::max(0, overlap.height - 2*bw));
@ -14543,30 +14582,32 @@ void MeshTexture::GlobalPatchColorAlignment(Image8U3& atlas, int textureSize) @@ -14543,30 +14582,32 @@ void MeshTexture::GlobalPatchColorAlignment(Image8U3& atlas, int textureSize)
}
// 应用:对每个 patch 的像素加上偏移(加性,限制幅度)
const float maxAdj = 30.0f; // 8-bit 空间最大偏移
#pragma omp parallel for
for (int i = 0; i < NP; ++i) {
const RCPatch& patch = rcPatches[i];
float adjR = std::max(-maxAdj, std::min(maxAdj, xR(i)));
float adjG = std::max(-maxAdj, std::min(maxAdj, xG(i)));
float adjB = std::max(-maxAdj, std::min(maxAdj, xB(i)));
for (int y = patch.rect.y; y < patch.rect.y + patch.rect.height; ++y) {
for (int x = patch.rect.x; x < patch.rect.x + patch.rect.width; ++x) {
if (x < 0 || x >= textureSize || y < 0 || y >= textureSize) continue;
Pixel8U& px = atlas(y, x);
if (px[0]==0 && px[1]==0 && px[2]==0) continue;
// BGR 存储顺序
px[2] = (uint8_t)CLAMP(px[2] + adjR, 0.f, 255.f); // R
px[1] = (uint8_t)CLAMP(px[1] + adjG, 0.f, 255.f); // G
px[0] = (uint8_t)CLAMP(px[0] + adjB, 0.f, 255.f); // B
}
}
}
const float maxAdj = 50.0f; // 8-bit 空间最大偏移
// 应用:对每个像素,如果它属于 patch i,才加偏移
#pragma omp parallel for
for (int y = 0; y < textureSize; ++y) {
for (int x = 0; x < textureSize; ++x) {
size_t idx = y * textureSize + x;
if (m_texelPatchID[idx] == NO_ID) continue;
int pid = m_texelPatchID[idx];
const float adjR = std::max(-maxAdj, std::min(maxAdj, xR(pid)));
const float adjG = std::max(-maxAdj, std::min(maxAdj, xG(pid)));
const float adjB = std::max(-maxAdj, std::min(maxAdj, xB(pid)));
Pixel8U& px = atlas(y, x);
if (px[0]==0 && px[1]==0 && px[2]==0) continue;
px[2] = (uint8_t)CLAMP(px[2] + adjR, 0.f, 255.f);
px[1] = (uint8_t)CLAMP(px[1] + adjG, 0.f, 255.f);
px[0] = (uint8_t)CLAMP(px[0] + adjB, 0.f, 255.f);
}
}
DEBUG_EXTRA("Global alignment done: %d constraints (%s)", rows, TD_TIMER_GET_FMT().c_str());
}
// ============================================================
// 3. RC 风格光栅化主函数(含接缝优化)
// ============================================================
// ============================================================
// 3. RC 风格光栅化主函数(含接缝优化)
// ============================================================
@ -14600,19 +14641,30 @@ bool MeshTexture::RasterizeVirtualFaces( @@ -14600,19 +14641,30 @@ bool MeshTexture::RasterizeVirtualFaces(
atlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r));
m_texelScores.assign(textureSize * textureSize, TexelScore{});
// ★ 修复:m_texelPatchID 只初始化一次,移到循环外面
m_texelPatchID.assign(textureSize * textureSize, NO_ID);
VERBOSE("[Raster] Step 1: initializing buffers... textureSize=%d, scores=%zu, patchID=%zu",
textureSize, m_texelScores.size(), m_texelPatchID.size());
if (!ComputeVirtualFaceGeometry(virtualFaceMap)) {
DEBUG_EXTRA("Failed to compute virtual face geometries"); return false;
}
currentTextureSize = textureSize;
// 确保增益已估计(若外部未调用则在此兜底)
if (!m_gainsEstimated) EstimateGlobalPhotometricCorrection();
// 3. 逐三角形光栅化(直接写入,最高分胜出 + 光度校正)
VERBOSE("[Raster] Step 2: starting patch loop, total faces=%zu", virtualFaceMap.size());
// 3. 逐三角形光栅化
#ifdef _USE_OPENMP
#pragma omp parallel for schedule(dynamic)
#endif
for (int i = 0; i < (int)virtualFaceMap.size(); ++i) {
if (i % 10000 == 0)
VERBOSE("[Raster] processing face %d / %d", i, (int)virtualFaceMap.size());
const VirtualFace& vf = virtualFaceMap[i];
const VirtualFaceGeometry& geom = m_virtualFaceGeometries[i];
if (!geom.isValid || vf.faces.empty() || virtualFaceViews[i].empty()) continue;
@ -14631,6 +14683,13 @@ bool MeshTexture::RasterizeVirtualFaces( @@ -14631,6 +14683,13 @@ bool MeshTexture::RasterizeVirtualFaces(
if (minX > maxX || minY > maxY) continue;
int patchW = maxX - minX + 1, patchH = maxY - minY + 1;
// ★ Guard:跳过异常巨大的 patch
if (patchW > textureSize || patchH > textureSize ||
(int64_t)patchW * patchH > (int64_t)textureSize * textureSize / 5) {
DEBUG_EXTRA("[Raster] face %d has HUGE rect: %dx%d, skipping", i, patchW, patchH);
continue;
}
cv::Mat mapX(patchH, patchW, CV_32FC1), mapY(patchH, patchW, CV_32FC1);
const float* H = geom.homography.ptr<float>();
for (int y = minY; y <= maxY; ++y) {
@ -14645,7 +14704,7 @@ bool MeshTexture::RasterizeVirtualFaces( @@ -14645,7 +14704,7 @@ bool MeshTexture::RasterizeVirtualFaces(
cv::Mat patch;
cv::remap(srcImg.image, patch, mapX, mapY, cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0,0,0));
// 应用光度校正(在 patch 上原地乘 gain)
// 应用光度校正
const cv::Vec3f& gain = (viewID < (IIndex)m_imageGains.size()) ? m_imageGains[viewID] : cv::Vec3f(1,1,1);
const bool applyGain = (gain != cv::Vec3f(1,1,1));
if (applyGain) {
@ -14653,9 +14712,9 @@ bool MeshTexture::RasterizeVirtualFaces( @@ -14653,9 +14712,9 @@ bool MeshTexture::RasterizeVirtualFaces(
for (int x = 0; x < patchW; ++x) {
cv::Vec3b& p = patch.at<cv::Vec3b>(y, x);
if (p[0]==0 && p[1]==0 && p[2]==0) continue;
p[0] = cv::saturate_cast<uchar>(p[0]*gain[0]); // B
p[1] = cv::saturate_cast<uchar>(p[1]*gain[1]); // G
p[2] = cv::saturate_cast<uchar>(p[2]*gain[2]); // R
p[0] = cv::saturate_cast<uchar>(p[0]*gain[0]);
p[1] = cv::saturate_cast<uchar>(p[1]*gain[1]);
p[2] = cv::saturate_cast<uchar>(p[2]*gain[2]);
}
}
}
@ -14670,16 +14729,22 @@ bool MeshTexture::RasterizeVirtualFaces( @@ -14670,16 +14729,22 @@ bool MeshTexture::RasterizeVirtualFaces(
int atlasX = x + minX, atlasY = y + minY;
if (atlasX<0||atlasX>=textureSize||atlasY<0||atlasY>=textureSize) continue;
size_t idx = atlasY * textureSize + atlasX;
// ★ 已删除错误放在这里的 m_texelPatchID.assign(...)
if (currentScore > m_texelScores[idx].score) {
atlas(atlasY, atlasX) = Pixel8U{color[2], color[1], color[0]};
m_texelScores[idx].score = currentScore;
m_texelScores[idx].viewID = viewID;
m_texelPatchID[idx] = i; // ★ 记录这个像素属于 patch i
}
}
}
}
}
VERBOSE("[Raster] Step 3: rasterization done, building RC patches...");
m_texelScores.clear();
DEBUG_EXTRA("RC-style Rasterization completed: %s", TD_TIMER_GET_FMT().c_str());
m_virtualFaceViewWeights = virtualFaceViewWeights;
@ -14701,21 +14766,25 @@ bool MeshTexture::RasterizeVirtualFaces( @@ -14701,21 +14766,25 @@ bool MeshTexture::RasterizeVirtualFaces(
}
DEBUG_EXTRA("Created %zu RC patches", rcPatches.size());
// ★ 统计每个 patch 的平均色(光栅化完成后调用)
// ★ 统计每个 patch 的平均色
const int NP = (int)rcPatches.size();
patchAvgColor.assign(NP, Color(0,0,0));
std::vector<int> pixelCount(NP, 0);
for (int i = 0; i < NP; ++i) {
if ((int64_t)rcPatches[i].rect.width * rcPatches[i].rect.height > (int64_t)textureSize * textureSize / 5) {
DEBUG_EXTRA("[Raster] patch %d HUGE rect in avgColor, skipping", i);
continue;
}
const RCPatch& patch = rcPatches[i];
for (int y = patch.rect.y; y < patch.rect.y + patch.rect.height; ++y) {
for (int x = patch.rect.x; x < patch.rect.x + patch.rect.width; ++x) {
if (x < 0 || x >= textureSize || y < 0 || y >= textureSize) continue;
const Pixel8U& px = atlas(y, x);
if (px[0]==0 && px[1]==0 && px[2]==0) continue; // 跳过空像素
patchAvgColor[i][0] += px[2]; // R
patchAvgColor[i][1] += px[1]; // G
patchAvgColor[i][2] += px[0]; // B
if (px[0]==0 && px[1]==0 && px[2]==0) continue;
patchAvgColor[i][0] += px[2];
patchAvgColor[i][1] += px[1];
patchAvgColor[i][2] += px[0];
pixelCount[i]++;
}
}
@ -14727,16 +14796,16 @@ bool MeshTexture::RasterizeVirtualFaces( @@ -14727,16 +14796,16 @@ bool MeshTexture::RasterizeVirtualFaces(
}
}
DEBUG_EXTRA("Patch avg color computed: %d patches, %d with pixels",
DEBUG_EXTRA("Patch avg color computed: %d patches, %d with pixels",
NP, NP - std::count(pixelCount.begin(), pixelCount.end(), 0));
// 5/6/7. 接缝
BuildSeamEdgesFromRCPatches();
if (!rcSeamEdges.empty()) {
DEBUG_EXTRA("Before GlobalAlign: rcSeamEdges=%zu, patchAvgColor=%zu",
rcSeamEdges.size(), patchAvgColor.size());
GlobalPatchColorAlignment(atlas, textureSize); // 先全局对齐
LocalSeamBlending(atlas, textureSize); // 再局部软化
DEBUG_EXTRA("Before GlobalAlign: rcSeamEdges=%zu, patchAvgColor=%zu",
rcSeamEdges.size(), patchAvgColor.size());
GlobalPatchColorAlignment(atlas, textureSize);
LocalSeamBlending(atlas, textureSize);
}
if (!seamEdges.empty()) {
@ -15683,7 +15752,7 @@ if (!g_avgColorsComputed) { @@ -15683,7 +15752,7 @@ if (!g_avgColorsComputed) {
// ========== 颜色一致性代价 ==========
{
const float lambda = 200.35f;
const float lambda = 20.35f;
std::vector<cv::Vec3f> neighborColors;
if (!scene.mesh.faceFaces.empty() && faceID < scene.mesh.faceFaces.size()) {
@ -15691,11 +15760,11 @@ if (!g_avgColorsComputed) { @@ -15691,11 +15760,11 @@ if (!g_avgColorsComputed) {
for (int k = 0; k < 3; ++k) {
FIndex nb = topoNeighbors[k];
if (nb == NO_ID || nb >= (FIndex)faceToView.size()) continue;
if (!scene.mesh.faceFaces.empty() && faceID < scene.mesh.faceFaces.size()) {
const SEACAVE::TPoint3<unsigned int>& nbrs = scene.mesh.faceFaces[faceID];
if (!scene.mesh.faceFaces.empty() && nb < scene.mesh.faceFaces.size()) {
const auto& nbrs = scene.mesh.faceFaces[nb];
for (int ni = 0; ni < 3; ++ni) {
unsigned int nbrFace = nbrs[ni];
if (nbrFace == 0xFFFFFFFF) continue; // 边界边没有邻居(NO_ID)
if (nbrFace == 0xFFFFFFFF) continue;
if (nbrFace >= (unsigned int)faceNeighbors.size()) continue;
if (faceNeighbors[nbrFace].empty()) continue;
@ -20235,6 +20304,8 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi @@ -20235,6 +20304,8 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
fRatioDataSmoothness, nIgnoreMaskLabel, views))
return false;
texture.SmoothVirtualFaceViews(texture.faceViews, mesh.faceFaces, 2);
// ✅ 4. RC 风格光栅化(Affine + Homography 混合)
Mesh::Image8U3Arr textures;
if (!texture.RasterizeVirtualFaces(

Loading…
Cancel
Save