Compare commits

...
28 Commits
Author SHA1 Message Date
hesuicong abb81f6bfd 解决一些数据兼容问题 2026-11-11 15:34:43 +08:00
hesuicong 200e13a7ba 对接怀江的调用接口 2026-11-11 10:17:47 +08:00
hesuicong 7a86495d6a 边做圆角 2026-11-10 17:33:46 +08:00
hesuicong efba6dbe29 填不空隙,平滑处理 2026-11-10 15:53:08 +08:00
hesuicong 38c0b9948a 优化眼镜填空 2026-11-09 11:42:05 +08:00
hesuicong 6f7805c78d 上传代码 2026-11-09 10:05:17 +08:00
hesuicong ac85d3a23c 平滑处理 2026-11-06 17:03:58 +08:00
hesuicong 1f272668f1 整理代码和平滑处理 2026-11-06 16:47:58 +08:00
hesuicong 22bfc8adfb 整理代码 2026-11-06 15:59:59 +08:00
hesuicong 17446d4053 添加眼镜优化代码 2026-11-06 14:57:38 +08:00
hesuicong 5b1b063bee 删除代码 2026-11-06 14:55:15 +08:00
hesuicong d5aa82f15e 眼镜生成代码添加 2026-11-06 14:53:51 +08:00
hesuicong 95570fede1 暗部提亮 2026-11-05 17:08:50 +08:00
hesuicong 4d3e0ee04f 暗部提亮 2026-11-04 17:31:23 +08:00
hesuicong d4330afd6f 暗部提亮测试代码 2026-10-31 15:56:07 +08:00
hesuicong 2f88885ccc 解决色差问题 2026-10-23 14:56:18 +08:00
hesuicong 07a4ade712 模糊和清晰平衡 2026-10-22 10:34:49 +08:00
hesuicong 433a06716a 进一步清晰化 2026-10-21 17:02:00 +08:00
hesuicong 875f0c5be5 进一步整理finalPatch逻辑 2026-10-21 16:45:39 +08:00
hesuicong 209fe6a260 finalPatch逻辑整理 2026-10-21 15:03:39 +08:00
hesuicong 74271d0ad6 填满小黄点 2026-10-21 11:13:44 +08:00
hesuicong 272d3900d0 清晰化 2026-10-20 11:24:50 +08:00
hesuicong 6b67c75c7e 解决缝隙问题和填色问题 2026-10-19 18:25:04 +08:00
hesuicong f7fccb2ae9 参数和逻辑微调 2026-10-19 11:19:42 +08:00
hesuicong c5f93374f4 解决缝隙问题 2026-10-17 18:01:01 +08:00
hesuicong 351a0a274f 解决矩形杂色和色差问题 2026-10-16 18:13:21 +08:00
hesuicong b106a7976e 解决掉矩形杂色问题,但是过渡有色差 2026-10-16 11:04:26 +08:00
hesuicong 05bae3ad90 注释打印图片 2026-10-15 11:09:38 +08:00
5 changed files with 5372 additions and 49 deletions

No files matched your search

+33
View File
@@ -78,3 +78,36 @@ color_change/__pycache__/calib_io.cpython-310.pyc
.gitignore .gitignore
.gitignore .gitignore
color_change/__pycache__/calibration.cpython-310.pyc color_change/__pycache__/calibration.cpython-310.pyc
fill2sc/632723.jpg
fill2sc/632723.mtl
fill2sc/fill_preview.png
fill2sc/fill_report.txt
.gitignore
fill2sc/index.txt
fill2sc/data/20261102/20261102_glasses_vertices.txt
fill2sc/data/20261102/20261102_glasses.blend
fill2sc/data/20261102/20261102.mtl
fill2sc/data/20261102/20261102.zip
fill2sc/data/20261102/20261102Tex1.jpg
fill2sc/data/20261102/fill_20261102_preview.png
fill2sc/data/20261102/index.txt
fill2sc/data/631230/631230_glasses_vertices.txt
fill2sc/data/631230/631230_glasses.blend
fill2sc/data/631230/631230.jpg
fill2sc/data/631230/631230.mtl
fill2sc/data/631230/631230.zip
fill2sc/data/631230/fill_631230_preview.png
fill2sc/data/631230/index.txt
fill2sc/data/632723/632723.jpg
fill2sc/data/632723/632723.mtl
fill2sc/data/632723/fill_632723_preview.png
fill2sc/data/632723/fill_preview.png
fill2sc/data/632723/fill_report.txt
fill2sc/data/632723/index.txt
fill2sc/data/636394/636394_glasses_vertices.txt
fill2sc/data/636394/636394_glasses.blend
fill2sc/data/636394/636394.jpg
fill2sc/data/636394/636394.mtl
fill2sc/data/636394/636394.zip
fill2sc/data/636394/fill_636394_preview.png
fill2sc/data/636394/index.txt
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+692 -49
View File
@@ -795,6 +795,16 @@ public:
const Mesh::TexCoordArr& existingTexcoords, // 添加已有UV参数 const Mesh::TexCoordArr& existingTexcoords, // 添加已有UV参数
const Mesh::TexIndexArr& existingTexindices // 添加已有纹理索引参数 const Mesh::TexIndexArr& existingTexindices // 添加已有纹理索引参数
); );
// 在 MeshTexture 类定义中添加
struct VertexColorInfo {
bool visibleInAnyView = false; // 至少在1个视图中可见
cv::Vec3f color; // 最终颜色(扩散后)
cv::Vec3f mainColor; // 主视图颜色
std::vector<int> neighborVertices; // 邻接顶点
};
std::vector<VertexColorInfo> m_vertexColors;
std::vector<uint32_t> faceToPatchID; // faceID → 新 patchID 映射 std::vector<uint32_t> faceToPatchID; // faceID → 新 patchID 映射
void MergeSameViewPatches(); void MergeSameViewPatches();
void BuildSeamEdgesFromFaceToPatchID(); void BuildSeamEdgesFromFaceToPatchID();
@@ -805,6 +815,11 @@ public:
Image8U3& atlas, Image8U3& atlas,
int textureSize); int textureSize);
void RunSeamLevelingOnAtlas(Image8U3& atlas, int textureSize); void RunSeamLevelingOnAtlas(Image8U3& atlas, int textureSize);
void PropagateMainViews(
const VirtualFaceMap& virtualFaceMap,
const std::vector<std::vector<IIndex>>& virtualFaceViews,
const std::vector<std::vector<float>>& virtualFaceViewWeights,
std::vector<IIndex>& faceViewsHighFreq);
bool RasterizeVirtualFaces( bool RasterizeVirtualFaces(
const VirtualFaceMap& virtualFaceMap, const VirtualFaceMap& virtualFaceMap,
const std::vector<std::vector<IIndex>>& virtualFaceViews, const std::vector<std::vector<IIndex>>& virtualFaceViews,
@@ -813,6 +828,17 @@ public:
Pixel8U colEmpty, Pixel8U colEmpty,
Mesh::Image8U3Arr& outTextures); Mesh::Image8U3Arr& outTextures);
inline bool ComputeBarycentric(float u, float v,
const TexCoord& uv0, const TexCoord& uv1, const TexCoord& uv2,
float& b0, float& b1, float& b2) {
float denom = (uv1.x - uv0.x) * (uv2.y - uv0.y) - (uv1.y - uv0.y) * (uv2.x - uv0.x);
if (std::abs(denom) < 1e-12f) return false;
b1 = ((u - uv0.x) * (uv2.y - uv0.y) - (v - uv0.y) * (uv2.x - uv0.x)) / denom;
b2 = ((u - uv0.x) * (uv1.y - uv0.y) - (v - uv0.y) * (uv1.x - uv0.x)) / denom;
b2 = ((v - uv0.y) * (uv1.x - uv0.x) - (u - uv0.x) * (uv1.y - uv0.y)) / denom;
b0 = 1.0f - b1 - b2;
return (b0 >= -1e-4f && b1 >= -1e-4f && b2 >= -1e-4f);
}
cv::Mat GetHomographyForView(int virtualFaceIdx, IIndex viewId) const; cv::Mat GetHomographyForView(int virtualFaceIdx, IIndex viewId) const;
void BuildStandardSeamDataFromRCPatches( void BuildStandardSeamDataFromRCPatches(
@@ -16281,6 +16307,96 @@ cv::Mat MeshTexture::GetHomographyForView(int virtualFaceIdx, IIndex viewId) con
return cv::Mat(); return cv::Mat();
} }
// 在光栅化之前,添加这个函数来传播主视图
void MeshTexture::PropagateMainViews(
const VirtualFaceMap& virtualFaceMap,
const std::vector<std::vector<IIndex>>& virtualFaceViews,
const std::vector<std::vector<float>>& virtualFaceViewWeights,
std::vector<IIndex>& faceViewsHighFreq)
{
// 1. 构建虚拟面的邻接关系(通过共享原始三角面)
std::vector<std::set<FIndex>> faceAdjacency(virtualFaceMap.size());
// 建立:原始三角面 -> 包含它的虚拟面列表
std::map<FIndex, std::vector<size_t>> faceToVirtualFaces;
for (size_t i = 0; i < virtualFaceMap.size(); ++i) {
for (FIndex fi : virtualFaceMap[i].faces) {
faceToVirtualFaces[fi].push_back(i);
}
}
// 构建邻接:共享同一个原始三角面的两个虚拟面视为相邻
for (const auto& entry : faceToVirtualFaces) {
const auto& virtFaces = entry.second;
for (size_t a = 0; a < virtFaces.size(); ++a) {
for (size_t b = a + 1; b < virtFaces.size(); ++b) {
faceAdjacency[virtFaces[a]].insert((FIndex)virtFaces[b]);
faceAdjacency[virtFaces[b]].insert((FIndex)virtFaces[a]);
}
}
}
// 2. 贪心传播:从覆盖面积最大的虚拟面开始,让邻居跟随它的主视图
// 先按覆盖面积排序
std::vector<size_t> sortedIndices(virtualFaceMap.size());
std::iota(sortedIndices.begin(), sortedIndices.end(), 0);
// 计算每个虚拟面的覆盖面积(UV空间面积)
std::vector<double> faceAreas(virtualFaceMap.size(), 0);
for (size_t i = 0; i < virtualFaceMap.size(); ++i) {
const VirtualFaceGeometry& geom = m_virtualFaceGeometries[i];
if (geom.isValid) {
faceAreas[i] = (geom.uvBounds.ptMax.x() - geom.uvBounds.ptMin.x()) *
(geom.uvBounds.ptMax.y() - geom.uvBounds.ptMin.y());
}
}
std::sort(sortedIndices.begin(), sortedIndices.end(),
[&](size_t a, size_t b) { return faceAreas[a] > faceAreas[b]; });
// 3. 传播
std::vector<bool> visited(virtualFaceMap.size(), false);
std::queue<size_t> queue;
for (size_t startIdx : sortedIndices) {
if (visited[startIdx]) continue;
if (faceViewsHighFreq[startIdx] == NO_ID) continue;
// 从大面积的面开始BFS传播
visited[startIdx] = true;
queue.push(startIdx);
while (!queue.empty()) {
size_t cur = queue.front();
queue.pop();
IIndex curView = faceViewsHighFreq[cur];
for (FIndex neighborIdx : faceAdjacency[cur]) {
if (visited[neighborIdx]) continue;
// 检查邻居是否也能看到 curView
bool neighborCanUseCurView = false;
for (size_t vi = 0; vi < virtualFaceViews[neighborIdx].size(); ++vi) {
if (virtualFaceViews[neighborIdx][vi] == curView) {
neighborCanUseCurView = true;
break;
}
}
if (neighborCanUseCurView) {
// 强制邻居使用相同的主视图
faceViewsHighFreq[neighborIdx] = curView;
visited[neighborIdx] = true;
queue.push(neighborIdx);
}
}
}
}
DEBUG_EXTRA("Main view propagation done: %zu faces processed", virtualFaceMap.size());
}
bool MeshTexture::RasterizeVirtualFaces( bool MeshTexture::RasterizeVirtualFaces(
const VirtualFaceMap& virtualFaceMap, const VirtualFaceMap& virtualFaceMap,
const std::vector<std::vector<IIndex>>& virtualFaceViews, const std::vector<std::vector<IIndex>>& virtualFaceViews,
@@ -16304,12 +16420,13 @@ bool MeshTexture::RasterizeVirtualFaces(
int textureSize = ComputeOptimalTextureSizeAdaptive(virtualFaceMap, virtualFaceViews, nTextureSizeMultiple); int textureSize = ComputeOptimalTextureSizeAdaptive(virtualFaceMap, virtualFaceViews, nTextureSizeMultiple);
if (textureSize < 1024) textureSize = 1024; if (textureSize < 1024) textureSize = 1024;
if (textureSize > 16384) textureSize = 16384; if (textureSize > 16384) textureSize = 16384;
textureSize = std::max(textureSize, 4096); textureSize = std::max(textureSize, 8192);
// 2. atlas + score // 2. atlas + score
outTextures.emplace_back(textureSize, textureSize); outTextures.emplace_back(textureSize, textureSize);
Image8U3& atlas = outTextures.back(); Image8U3& atlas = outTextures.back();
atlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); atlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r));
m_texelScores.assign(textureSize * textureSize, TexelScore{}); m_texelScores.assign(textureSize * textureSize, TexelScore{});
m_texelPatchID.assign(textureSize * textureSize, NO_ID); m_texelPatchID.assign(textureSize * textureSize, NO_ID);
@@ -16321,11 +16438,40 @@ bool MeshTexture::RasterizeVirtualFaces(
} }
currentTextureSize = textureSize; currentTextureSize = textureSize;
VERBOSE("[Raster] Propagating main views for consistency...");
PropagateMainViews(virtualFaceMap, virtualFaceViews, virtualFaceViewWeights, faceViewsHighFreq);
if (!m_gainsEstimated) EstimateGlobalPhotometricCorrection(); if (!m_gainsEstimated) EstimateGlobalPhotometricCorrection();
// ★★★ 提前初始化顶点颜色结构 ★★★
m_vertexColors.resize(scene.mesh.vertices.size());
for (auto& vc : m_vertexColors) {
vc.visibleInAnyView = false;
vc.color = cv::Vec3f(0, 0, 0);
vc.mainColor = cv::Vec3f(0, 0, 0);
vc.neighborVertices.clear();
}
// 建立顶点邻接关系
for (FIndex fi = 0; fi < faces.size(); ++fi) {
const Face& f = faces[fi];
for (int j = 0; j < 3; ++j) {
int v0 = f[j];
int v1 = f[(j + 1) % 3];
auto& nb0 = m_vertexColors[v0].neighborVertices;
if (std::find(nb0.begin(), nb0.end(), v1) == nb0.end())
nb0.push_back(v1);
auto& nb1 = m_vertexColors[v1].neighborVertices;
if (std::find(nb1.begin(), nb1.end(), v0) == nb1.end())
nb1.push_back(v0);
}
}
VERBOSE("[Raster] Step 2: starting patch loop, total faces=%zu", virtualFaceMap.size()); VERBOSE("[Raster] Step 2: starting patch loop, total faces=%zu", virtualFaceMap.size());
// ========== 新增:预加载所有图像到浮点缓存(加速多次采样)========== // ========== 预加载所有图像到浮点缓存 ==========
std::vector<cv::Mat> imagesFloat(images.size()); std::vector<cv::Mat> imagesFloat(images.size());
for (size_t vi = 0; vi < images.size(); vi++) { for (size_t vi = 0; vi < images.size(); vi++) {
if (!images[vi].image.empty()) { if (!images[vi].image.empty()) {
@@ -16347,13 +16493,12 @@ bool MeshTexture::RasterizeVirtualFaces(
if (!geom.isValid || vf.faces.empty() || virtualFaceViews[i].empty()) continue; if (!geom.isValid || vf.faces.empty() || virtualFaceViews[i].empty()) continue;
// ========== 分频带视图获取 ========== // ========== 分频带视图获取 ==========
IIndex mainView = faceViewsHighFreq[i]; // 高频视图(主视图) IIndex mainView = faceViewsHighFreq[i];
const auto& lowFreqViews = faceViewsLowFreq[i]; // 低频视图列表 const auto& lowFreqViews = faceViewsLowFreq[i];
if (mainView == NO_ID || mainView >= (IIndex)images.size()) continue; if (mainView == NO_ID || mainView >= (IIndex)images.size()) continue;
if (images[mainView].image.empty()) continue; if (images[mainView].image.empty()) continue;
// 确保低频视图也有效
bool hasLowFreqViews = false; bool hasLowFreqViews = false;
for (const auto& lfv : lowFreqViews) { for (const auto& lfv : lowFreqViews) {
if (lfv.viewId < (IIndex)images.size() && !images[lfv.viewId].image.empty()) { if (lfv.viewId < (IIndex)images.size() && !images[lfv.viewId].image.empty()) {
@@ -16378,50 +16523,116 @@ bool MeshTexture::RasterizeVirtualFaces(
continue; continue;
} }
// ========== 构建主视图的映射 ========== // ========== 用重心坐标 + 3D 投影 ==========
cv::Mat mapX(patchH, patchW, CV_32FC1), mapY(patchH, patchW, CV_32FC1); cv::Mat mapX(patchH, patchW, CV_32FC1), mapY(patchH, patchW, CV_32FC1);
mapX.setTo(-1);
mapY.setTo(-1);
if (vf.faces.empty()) continue;
FIndex faceIdx = vf.faces[0];
const Face& face = scene.mesh.faces[faceIdx];
Point3f v[3];
TexCoord uv[3];
for (int j = 0; j < 3; ++j) {
v[j] = scene.mesh.vertices[face[j]];
uv[j] = scene.mesh.faceTexcoords[faceIdx * 3 + j];
}
const Camera& cam = images[mainView].camera;
bool camValid = (cam.K(0,0) > 0 && cam.K(1,1) > 0);
if (!camValid) {
const float* H = geom.homography.ptr<float>(); const float* H = geom.homography.ptr<float>();
for (int y = minY; y <= maxY; ++y) { for (int y = minY; y <= maxY; ++y) {
for (int x = minX; x <= maxX; ++x) { for (int x = minX; x <= maxX; ++x) {
float u = (float)x / (float)textureSize, v = (float)y / (float)textureSize; float u = (float)x / (float)textureSize, v_coord = (float)y / (float)textureSize;
float w = H[6]*u + H[7]*v + H[8]; float w = H[6]*u + H[7]*v_coord + H[8];
if (std::abs(w) < 1e-12f) { mapX.at<float>(y-minY,x-minX)=-1; mapY.at<float>(y-minY,x-minX)=-1; continue; } if (std::abs(w) < 1e-12f) { mapX.at<float>(y-minY,x-minX)=-1; mapY.at<float>(y-minY,x-minX)=-1; continue; }
mapX.at<float>(y-minY,x-minX) = (H[0]*u+H[1]*v+H[2])/w; mapX.at<float>(y-minY,x-minX) = (H[0]*u+H[1]*v_coord+H[2])/w;
mapY.at<float>(y-minY,x-minX) = (H[3]*u+H[4]*v+H[5])/w; mapY.at<float>(y-minY,x-minX) = (H[3]*u+H[4]*v_coord+H[5])/w;
}
}
} else {
for (int py = minY; py <= maxY; ++py) {
for (int px = minX; px <= maxX; ++px) {
float u = (float)px / (float)textureSize;
float v_coord = (float)py / (float)textureSize;
float b0, b1, b2;
bool inside = ComputeBarycentric(u, v_coord, uv[0], uv[1], uv[2], b0, b1, b2);
if (!inside) continue;
Point3f P = v[0] * b0 + v[1] * b1 + v[2] * b2;
Point2f imgPt = cam.ProjectPointP(P);
if (imgPt.x < 0 || imgPt.x >= images[mainView].image.cols ||
imgPt.y < 0 || imgPt.y >= images[mainView].image.rows) {
continue;
}
mapX.at<float>(py - minY, px - minX) = imgPt.x;
mapY.at<float>(py - minY, px - minX) = imgPt.y;
}
} }
} }
// ========== 从主视图采样(全分辨率)========== // ========== 从主视图采样 ==========
cv::Mat mainPatch; cv::Mat mainPatch;
cv::remap(images[mainView].image, mainPatch, mapX, mapY, cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0,0,0)); cv::remap(images[mainView].image, mainPatch, mapX, mapY, cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0,0,0));
// ========== 从辅助视图采样(各自用自己的 homography)========== // ========== 从辅助视图采样 ==========
cv::Mat lowFreqPatch(patchH, patchW, CV_32FC3, cv::Scalar(0,0,0)); cv::Mat lowFreqPatch(patchH, patchW, CV_32FC3, cv::Scalar(0,0,0));
if (hasLowFreqViews) { if (hasLowFreqViews) {
float totalWeight = 0; float totalWeight = 0;
for (const auto& lfv : lowFreqViews) { for (const auto& lfv : lowFreqViews) {
if (lfv.viewId >= (IIndex)images.size() || images[lfv.viewId].image.empty()) continue; if (lfv.viewId >= (IIndex)images.size() || images[lfv.viewId].image.empty()) continue;
// ===== 获取该视图自己的 homography ===== const Camera& auxCam = images[lfv.viewId].camera;
const auto& geom = m_virtualFaceGeometries[i]; bool auxCamValid = (auxCam.K(0,0) > 0 && auxCam.K(1,1) > 0);
auto it = geom.viewHomographies.find(lfv.viewId);
if (it == geom.viewHomographies.end()) continue;
const cv::Mat& H_aux = it->second;
// ===== 用该视图的 homography 构建映射 =====
cv::Mat auxMapX(patchH, patchW, CV_32FC1), auxMapY(patchH, patchW, CV_32FC1); cv::Mat auxMapX(patchH, patchW, CV_32FC1), auxMapY(patchH, patchW, CV_32FC1);
const float* H = H_aux.ptr<float>(); auxMapX.setTo(-1);
auxMapY.setTo(-1);
if (!auxCamValid) {
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>();
for (int y = minY; y <= maxY; ++y) { for (int y = minY; y <= maxY; ++y) {
for (int x = minX; x <= maxX; ++x) { for (int x = minX; x <= maxX; ++x) {
float u = (float)x / (float)textureSize, v = (float)y / (float)textureSize; float u = (float)x / (float)textureSize, v_coord = (float)y / (float)textureSize;
float w = H[6]*u + H[7]*v + H[8]; float w = H[6]*u + H[7]*v_coord + H[8];
if (std::abs(w) < 1e-12f) { if (std::abs(w) < 1e-12f) {
auxMapX.at<float>(y-minY,x-minX) = -1; auxMapX.at<float>(y-minY,x-minX) = -1;
auxMapY.at<float>(y-minY,x-minX) = -1; auxMapY.at<float>(y-minY,x-minX) = -1;
continue; continue;
} }
auxMapX.at<float>(y-minY,x-minX) = (H[0]*u+H[1]*v+H[2])/w; auxMapX.at<float>(y-minY,x-minX) = (H[0]*u+H[1]*v_coord+H[2])/w;
auxMapY.at<float>(y-minY,x-minX) = (H[3]*u+H[4]*v+H[5])/w; auxMapY.at<float>(y-minY,x-minX) = (H[3]*u+H[4]*v_coord+H[5])/w;
}
}
} else {
for (int py = minY; py <= maxY; ++py) {
for (int px = minX; px <= maxX; ++px) {
float u = (float)px / (float)textureSize;
float v_coord = (float)py / (float)textureSize;
float b0, b1, b2;
bool inside = ComputeBarycentric(u, v_coord, uv[0], uv[1], uv[2], b0, b1, b2);
if (!inside) continue;
Point3f P = v[0] * b0 + v[1] * b1 + v[2] * b2;
Point2f imgPt = auxCam.ProjectPointP(P);
if (imgPt.x < 0 || imgPt.x >= images[lfv.viewId].image.cols ||
imgPt.y < 0 || imgPt.y >= images[lfv.viewId].image.rows) {
continue;
}
auxMapX.at<float>(py - minY, px - minX) = imgPt.x;
auxMapY.at<float>(py - minY, px - minX) = imgPt.y;
}
} }
} }
@@ -16448,23 +16659,133 @@ bool MeshTexture::RasterizeVirtualFaces(
lowFreqPatch = mainFloat.clone(); lowFreqPatch = mainFloat.clone();
} }
// ========== 从主视图提取高频 ========== // ========== 多视图高频混合 ==========
// 对主视图做高斯模糊得到低频,然后相减得高频
cv::Mat mainFloat; cv::Mat mainFloat;
mainPatch.convertTo(mainFloat, CV_32FC3, 1.0f / 255.0f); mainPatch.convertTo(mainFloat, CV_32FC3, 1.0f / 255.0f);
cv::Mat mainLowFreq; cv::Mat mainLowFreq;
cv::GaussianBlur(mainFloat, mainLowFreq, cv::Size(31, 31), 10.0); // σ=10 cv::GaussianBlur(mainFloat, mainLowFreq, cv::Size(5, 5), 1.5);
cv::Mat highFreqAccum = (mainFloat - mainLowFreq) * 0.9f;
float highFreqTotalWeight = 0.6f;
cv::Mat highFreq = mainFloat - mainLowFreq; // 高频 = 原图 - 低频 if (hasLowFreqViews) {
for (const auto& lfv : lowFreqViews) {
if (lfv.viewId == mainView) continue;
if (lfv.viewId >= (IIndex)images.size() || images[lfv.viewId].image.empty()) continue;
// 在合成之前,对 lowFreqPatch 再做一次轻微平滑 cv::Mat auxPatch;
cv::GaussianBlur(lowFreqPatch, lowFreqPatch, cv::Size(5, 5), 1.0); cv::remap(images[lfv.viewId].image, auxPatch, mapX, mapY,
cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0,0,0));
// ========== 合成:低频用多视图融合,高频用主视图细节 ========== cv::Mat auxFloat;
cv::Mat finalPatch = lowFreqPatch + highFreq; auxPatch.convertTo(auxFloat, CV_32FC3, 1.0f / 255.0f);
// cv::Mat finalPatch = highFreq.clone();
// finalPatch = cv::Mat::zeros(finalPatch.size(), finalPatch.type()); cv::Mat auxLowFreq;
cv::GaussianBlur(auxFloat, auxLowFreq, cv::Size(31, 31), 10.0);
float auxWeight = lfv.weight * 0.4f;
highFreqAccum += (auxFloat - auxLowFreq) * auxWeight;
highFreqTotalWeight += auxWeight;
}
}
if (highFreqTotalWeight > 0) {
highFreqAccum /= highFreqTotalWeight;
}
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)};
// 检查主视图
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<cv::Vec3f>(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>();
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<cv::Vec3f>((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];
}
}
}
// ========== 空洞填充与合成 ==========
cv::Mat filledLowFreq = lowFreqPatch.clone();
for (int y = 0; y < patchH; ++y) {
for (int x = 0; x < patchW; ++x) {
cv::Vec3f color = filledLowFreq.at<cv::Vec3f>(y, x);
if (color[0] == 0.0f && color[1] == 0.0f && color[2] == 0.0f) {
filledLowFreq.at<cv::Vec3f>(y, x) = mainFloat.at<cv::Vec3f>(y, x);
}
}
}
cv::Mat finalPatch = filledLowFreq * 0.8f + mainFloat * 0.2f;
// cv::Mat finalPatch = mainFloat;
// 钳制到 [0, 1] // 钳制到 [0, 1]
cv::Mat clampedPatch; cv::Mat clampedPatch;
@@ -16475,7 +16796,7 @@ bool MeshTexture::RasterizeVirtualFaces(
cv::Mat finalPatch8U; cv::Mat finalPatch8U;
clampedPatch.convertTo(finalPatch8U, CV_8UC3, 255.0f); clampedPatch.convertTo(finalPatch8U, CV_8UC3, 255.0f);
// 在写入 atlas 时,对边界像素做衰减混合 // ========== 写入 atlas ==========
#pragma omp critical (atlas_write) #pragma omp critical (atlas_write)
{ {
for (int y = 0; y < patchH; ++y) { for (int y = 0; y < patchH; ++y) {
@@ -16488,29 +16809,18 @@ bool MeshTexture::RasterizeVirtualFaces(
if (atlasX<0||atlasX>=textureSize||atlasY<0||atlasY>=textureSize) continue; if (atlasX<0||atlasX>=textureSize||atlasY<0||atlasY>=textureSize) continue;
size_t idx = atlasY * textureSize + atlasX; size_t idx = atlasY * textureSize + atlasX;
// ===== 新增:边界衰减权重 ===== // 边界衰减权重
float weight = 1.0f; float weight = 1.0f;
float distToEdgeX = std::min((float)x, (float)(patchW - 1 - x)); float distToEdgeX = std::min((float)x, (float)(patchW - 1 - x));
float distToEdgeY = std::min((float)y, (float)(patchH - 1 - y)); float distToEdgeY = std::min((float)y, (float)(patchH - 1 - y));
float minDist = std::min(distToEdgeX, distToEdgeY); float minDist = std::min(distToEdgeX, distToEdgeY);
if (minDist < pad) { if (minDist < pad) {
// 边界 8 像素内做线性衰减
weight = minDist / (float)pad; weight = minDist / (float)pad;
weight = weight * weight; // 平方衰减更平滑 weight = weight * weight;
} }
// 如果已有更高分的 texel,跳过(保持高分优先)
if (currentScore > m_texelScores[idx].score) { if (currentScore > m_texelScores[idx].score) {
// 如果有旧值,做加权混合
if (m_texelScores[idx].score > 0 && weight < 1.0f) {
cv::Vec3b oldColor = atlas.at<cv::Vec3b>(atlasY, atlasX);
float oldWeight = 1.0f - weight;
color[0] = (uchar)(color[0] * weight + oldColor[0] * oldWeight);
color[1] = (uchar)(color[1] * weight + oldColor[1] * oldWeight);
color[2] = (uchar)(color[2] * weight + oldColor[2] * oldWeight);
}
atlas.at<cv::Vec3b>(atlasY, atlasX) = color; atlas.at<cv::Vec3b>(atlasY, atlasX) = color;
m_texelScores[idx].score = currentScore; m_texelScores[idx].score = currentScore;
m_texelScores[idx].viewID = mainView; m_texelScores[idx].viewID = mainView;
@@ -16521,6 +16831,222 @@ bool MeshTexture::RasterizeVirtualFaces(
} }
} }
// ========== OpenMP 循环结束,执行顶点级别颜色扩散 ==========
// BFS扩散
std::queue<VIndex> bfsQueue;
std::vector<bool> visited(m_vertexColors.size(), false);
for (VIndex vi = 0; vi < m_vertexColors.size(); ++vi) {
if (m_vertexColors[vi].visibleInAnyView) {
m_vertexColors[vi].color = m_vertexColors[vi].mainColor;
visited[vi] = true;
bfsQueue.push(vi);
}
}
while (!bfsQueue.empty()) {
VIndex cur = bfsQueue.front();
bfsQueue.pop();
for (int nb : m_vertexColors[cur].neighborVertices) {
if (!visited[nb]) {
m_vertexColors[nb].color = m_vertexColors[cur].color;
visited[nb] = true;
bfsQueue.push(nb);
}
}
}
// 处理孤立顶点
for (VIndex vi = 0; vi < m_vertexColors.size(); ++vi) {
if (!visited[vi]) {
cv::Vec3f avgColor(0, 0, 0);
int count = 0;
for (int nb : m_vertexColors[vi].neighborVertices) {
if (visited[nb]) {
avgColor += m_vertexColors[nb].color;
count++;
}
}
if (count > 0) {
m_vertexColors[vi].color = avgColor / (float)count;
} else {
m_vertexColors[vi].color = cv::Vec3f(0.35f, 0.65f, 0.95f);
}
}
}
// ★★★ 在这里插入:强制给仍然为黑色的顶点赋色 ★★★
for (VIndex vi = 0; vi < m_vertexColors.size(); ++vi) {
if (m_vertexColors[vi].color[0] == 0.0f &&
m_vertexColors[vi].color[1] == 0.0f &&
m_vertexColors[vi].color[2] == 0.0f) {
cv::Vec3f avgColor(0, 0, 0);
int count = 0;
for (int nb : m_vertexColors[vi].neighborVertices) {
if (m_vertexColors[nb].color[0] > 0.0f ||
m_vertexColors[nb].color[1] > 0.0f ||
m_vertexColors[nb].color[2] > 0.0f) {
avgColor += m_vertexColors[nb].color;
count++;
}
}
if (count > 0) {
m_vertexColors[vi].color = avgColor / (float)count;
} else {
// 实在找不到,用主视图平均色
m_vertexColors[vi].color = cv::Vec3f(0.35f, 0.55f, 0.75f); // 灰色保底
}
}
}
DEBUG_EXTRA("Vertex color diffusion: %zu vertices, %zu visible, %zu invisible",
m_vertexColors.size(),
std::count(visited.begin(), visited.end(), true),
std::count(visited.begin(), visited.end(), false));
// ========== 用顶点颜色修复空洞 ==========
#ifdef _USE_OPENMP
#pragma omp parallel for schedule(dynamic)
#endif
for (int atlasY = 0; atlasY < textureSize; ++atlasY) {
for (int atlasX = 0; atlasX < textureSize; ++atlasX) {
size_t idx = atlasY * textureSize + atlasX;
// 只处理空洞像素
Pixel8U px = atlas(atlasY, atlasX);
if (px[0] != colEmpty.b || px[1] != colEmpty.g || px[2] != colEmpty.r)
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; // 实在找不到归属面,跳过
}
}
const VirtualFace& vf = virtualFaceMap[patchID];
if (vf.faces.empty()) continue;
FIndex faceIdx = vf.faces[0];
const Face& face = scene.mesh.faces[faceIdx];
// 获取顶点颜色
cv::Vec3f vertexColors[3];
for (int j = 0; j < 3; ++j) {
VIndex vi = face[j];
vertexColors[j] = (vi < m_vertexColors.size()) ?
m_vertexColors[vi].color : cv::Vec3f(0.5f, 0.5f, 0.5f);
}
// 计算 UV 坐标
float u = (float)atlasX / (float)textureSize;
float v_coord = (float)atlasY / (float)textureSize;
// 获取该面的 UV
TexCoord uv[3];
for (int j = 0; j < 3; ++j) {
uv[j] = scene.mesh.faceTexcoords[faceIdx * 3 + j];
}
// 重心坐标插值
float b0, b1, b2;
bool inside = ComputeBarycentric(u, v_coord, uv[0], uv[1], uv[2], b0, b1, b2);
cv::Vec3f fillColor;
if (inside) {
fillColor = vertexColors[0] * b0 + vertexColors[1] * b1 + vertexColors[2] * b2;
} else {
// 最近顶点
float minDist = FLT_MAX;
int bestVertex = 0;
for (int j = 0; j < 3; ++j) {
float du = u - uv[j].x;
float dv = v_coord - uv[j].y;
float dist = du * du + dv * dv;
if (dist < minDist) { minDist = dist; bestVertex = j; }
}
fillColor = vertexColors[bestVertex];
}
// ★★★ 新增:检查 fillColor 是否有效(接近黑色)★★★
bool fillIsBlack = (fillColor[0] < 0.05f && fillColor[1] < 0.05f && fillColor[2] < 0.05f);
if (fillIsBlack) {
// Fallback:从邻近非空洞像素采样
cv::Vec3f sampledColor(0, 0, 0);
int sampleCount = 0;
int sampleRad = 5;
for (int dy = -sampleRad; dy <= sampleRad && sampleCount < 16; ++dy) {
for (int dx = -sampleRad; dx <= sampleRad && sampleCount < 16; ++dx) {
int sx = atlasX + dx, sy = atlasY + dy;
if (sx < 0 || sx >= textureSize || sy < 0 || sy >= textureSize) continue;
Pixel8U spx = atlas(sy, sx);
// 检查是否是非空洞像素(与 colEmpty 比较)
if (spx[0] != colEmpty.b || spx[1] != colEmpty.g || spx[2] != colEmpty.r) {
sampledColor[0] += spx[2] / 255.0f; // R
sampledColor[1] += spx[1] / 255.0f; // G
sampledColor[2] += spx[0] / 255.0f; // B
sampleCount++;
}
}
}
if (sampleCount > 0) {
fillColor = sampledColor / (float)sampleCount;
} else {
// 实在不行,用该面的平均色
if (patchID < (int)patchAvgColor.size()) {
fillColor = cv::Vec3f(
patchAvgColor[patchID][0] / 255.0f,
patchAvgColor[patchID][1] / 255.0f,
patchAvgColor[patchID][2] / 255.0f
);
} else {
fillColor = cv::Vec3f(0.50f, 0.55f, 0.65f); // 最终保底
}
}
}
// 钳制并写入
fillColor[0] = std::max(0.0f, std::min(1.0f, fillColor[0]));
fillColor[1] = std::max(0.0f, std::min(1.0f, fillColor[1]));
fillColor[2] = std::max(0.0f, std::min(1.0f, fillColor[2]));
atlas(atlasY, atlasX) = Pixel8U(
(uchar)(fillColor[2] * 255.0f),
(uchar)(fillColor[1] * 255.0f),
(uchar)(fillColor[0] * 255.0f)
);
}
}
VERBOSE("[Raster] Step 3: rasterization done, building RC patches..."); VERBOSE("[Raster] Step 3: rasterization done, building RC patches...");
m_texelScores.clear(); m_texelScores.clear();
@@ -16544,7 +17070,7 @@ bool MeshTexture::RasterizeVirtualFaces(
} }
DEBUG_EXTRA("Created %zu RC patches", rcPatches.size()); DEBUG_EXTRA("Created %zu RC patches", rcPatches.size());
// ★ 统计每个 patch 的平均色 // 统计每个 patch 的平均色
const int NP = (int)rcPatches.size(); const int NP = (int)rcPatches.size();
patchAvgColor.assign(NP, Color(0,0,0)); patchAvgColor.assign(NP, Color(0,0,0));
std::vector<int> pixelCount(NP, 0); std::vector<int> pixelCount(NP, 0);
@@ -16586,7 +17112,124 @@ bool MeshTexture::RasterizeVirtualFaces(
if (m_texelPatchID[i] != NO_ID) validPixels++; 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::imwrite("/home/algo/Documents/openMVS/data/546893/out.546893/debug_atlas_before_return.png", atlas); // ★★★ 全局空洞终极填充(优化版)★★★
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<uchar>(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<uchar>(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<uchar>(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<uchar>(y, x)) {
atlas(y, x) = avgPx;
}
}
}
// ========== 内置 AO 计算与暗部提亮 ==========
// 先释放不再需要的 RC patches 大内存
{
std::vector<RCPatch>().swap(rcPatches);
std::vector<unsigned int>().swap(m_texelPatchID);
DEBUG_EXTRA("[AO] Freed RC patches and texelPatchID memory");
}
if (false)
{
DEBUG_EXTRA("[AO] Loading pre-baked AO from Blender...");
std::string aoPath = "/home/algo/Documents/openMVS/data/603454/out.603454/aon.jpg";
cv::Mat aoMapSmall = cv::imread(aoPath, cv::IMREAD_GRAYSCALE);
if (aoMapSmall.empty()) {
DEBUG_EXTRA("[AO] Failed to load AO map from %s, skipping", aoPath.c_str());
} else {
cv::resize(aoMapSmall, aoMapSmall, cv::Size(1024, 1024), 0, 0, cv::INTER_LINEAR);
aoMapSmall.convertTo(aoMapSmall, CV_32FC1, 1.0f / 255.0f);
cv::Mat aoMapFull;
cv::resize(aoMapSmall, aoMapFull, cv::Size(textureSize, textureSize), 0, 0, cv::INTER_LINEAR);
cv::Mat atlasFloat;
atlas.convertTo(atlasFloat, CV_32FC3, 1.0f / 255.0f);
float strength = 0.38f;
float aoDarkThresh = 0.62f;
float exponent = 0.92f;
for (int y = 0; y < textureSize; ++y) {
for (int x = 0; x < textureSize; ++x) {
float aoVal = aoMapFull.at<float>(y, x);
if (aoVal >= aoDarkThresh) continue;
cv::Vec3f color = atlasFloat.at<cv::Vec3f>(y, x);
float lum = 0.299f * color[2] + 0.587f * color[1] + 0.114f * color[0];
if (lum < 0.04f) continue; // 只保护最暗的材质
// ★★★ 线性映射,不加 cap ★★★
float factor = (aoDarkThresh - aoVal) / aoDarkThresh;
factor = std::pow(factor, exponent);
float add = factor * strength;
color[0] = std::min(1.0f, color[0] + add);
color[1] = std::min(1.0f, color[1] + add);
color[2] = std::min(1.0f, color[2] + add);
atlasFloat.at<cv::Vec3f>(y, x) = color;
}
}
atlasFloat.convertTo(atlas, CV_8UC3, 255.0f);
DEBUG_EXTRA("[AO] AO-based lightening applied using Blender AO map");
}
}
return true; return true;
} }