|
|
|
@ -1303,6 +1303,8 @@ public: |
|
|
|
// ---- 全局光度校正(per-image gain,3 通道独立)----
|
|
|
|
// ---- 全局光度校正(per-image gain,3 通道独立)----
|
|
|
|
std::vector<cv::Vec3f> m_imageGains; // 每个视图一个 (gR,gG,gB)
|
|
|
|
std::vector<cv::Vec3f> m_imageGains; // 每个视图一个 (gR,gG,gB)
|
|
|
|
bool m_gainsEstimated = false; |
|
|
|
bool m_gainsEstimated = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool s_gainsApplied = false; |
|
|
|
bool EstimateGlobalPhotometricCorrection(); |
|
|
|
bool EstimateGlobalPhotometricCorrection(); |
|
|
|
|
|
|
|
|
|
|
|
inline cv::Vec3f SampleColorAtPoint(const cv::Mat& img, const Camera& cam, const Point3f& pt3D) { |
|
|
|
inline cv::Vec3f SampleColorAtPoint(const cv::Mat& img, const Camera& cam, const Point3f& pt3D) { |
|
|
|
@ -9520,15 +9522,17 @@ void MeshTexture::GlobalSeamLeveling4() |
|
|
|
ASSERT(face[v] < vertpatch2rows.size()); |
|
|
|
ASSERT(face[v] < vertpatch2rows.size()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ★ UV [0,1] → patch 局部像素坐标
|
|
|
|
// // ★ UV [0,1] → patch 局部像素坐标
|
|
|
|
const cv::Size patchSize = texturePatch.rect.size(); |
|
|
|
// const cv::Size patchSize = texturePatch.rect.size();
|
|
|
|
TexCoord localTri[3]; |
|
|
|
// TexCoord localTri[3];
|
|
|
|
const TexCoord* uvTri = faceTexcoords.Begin() + idxFace * 3; |
|
|
|
// const TexCoord* uvTri = faceTexcoords.Begin() + idxFace * 3;
|
|
|
|
for (int v = 0; v < 3; ++v) { |
|
|
|
// for (int v = 0; v < 3; ++v) {
|
|
|
|
localTri[v] = TexCoord(uvTri[v].x * patchSize.width, |
|
|
|
// localTri[v] = TexCoord(uvTri[v].x * patchSize.width,
|
|
|
|
uvTri[v].y * patchSize.height); |
|
|
|
// uvTri[v].y * patchSize.height);
|
|
|
|
} |
|
|
|
// }
|
|
|
|
data.tri = localTri; |
|
|
|
// data.tri = localTri;
|
|
|
|
|
|
|
|
// 改回直接用,不乘
|
|
|
|
|
|
|
|
data.tri = faceTexcoords.Begin() + idxFace * 3; |
|
|
|
|
|
|
|
|
|
|
|
for (int v = 0; v < 3; ++v) { |
|
|
|
for (int v = 0; v < 3; ++v) { |
|
|
|
ASSERT(face[v] < vertices.size()); |
|
|
|
ASSERT(face[v] < vertices.size()); |
|
|
|
@ -9555,7 +9559,15 @@ void MeshTexture::GlobalSeamLeveling4() |
|
|
|
} |
|
|
|
} |
|
|
|
const Pixel8U& v = srcROI.at<Pixel8U>(r, c); |
|
|
|
const Pixel8U& v = srcROI.at<Pixel8U>(r, c); |
|
|
|
const Color col(RGB2YCBCR(Color(v))); |
|
|
|
const Color col(RGB2YCBCR(Color(v))); |
|
|
|
const Color acol(YCBCR2RGB(Color(col + a))); |
|
|
|
|
|
|
|
|
|
|
|
// const Color acol(YCBCR2RGB(Color(col + a)));
|
|
|
|
|
|
|
|
const float GAIN = 10.0f; // ★ 增益,可调(5~20)
|
|
|
|
|
|
|
|
const Color acol(YCBCR2RGB(Color( |
|
|
|
|
|
|
|
col[0] + a[0] * 1.0f, // Y 微调
|
|
|
|
|
|
|
|
col[1] + a[1] * GAIN, // Cb 大幅调
|
|
|
|
|
|
|
|
col[2] + a[2] * GAIN // Cr 大幅调
|
|
|
|
|
|
|
|
))); |
|
|
|
|
|
|
|
|
|
|
|
outPatch.at<cv::Vec3b>(r, c) = cv::Vec3b( |
|
|
|
outPatch.at<cv::Vec3b>(r, c) = cv::Vec3b( |
|
|
|
(uint8_t)CLAMP(ROUND2INT(acol[0]), 0, 255), |
|
|
|
(uint8_t)CLAMP(ROUND2INT(acol[0]), 0, 255), |
|
|
|
(uint8_t)CLAMP(ROUND2INT(acol[1]), 0, 255), |
|
|
|
(uint8_t)CLAMP(ROUND2INT(acol[1]), 0, 255), |
|
|
|
@ -15661,7 +15673,6 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
atlas = localAtlas; |
|
|
|
|
|
|
|
// // 调试用:导出调整后的 atlas
|
|
|
|
// // 调试用:导出调整后的 atlas
|
|
|
|
// static int dbgCount = 0;
|
|
|
|
// static int dbgCount = 0;
|
|
|
|
// char dbgName[256];
|
|
|
|
// char dbgName[256];
|
|
|
|
@ -15669,6 +15680,7 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( |
|
|
|
// cv::imwrite(dbgName, atlas);
|
|
|
|
// cv::imwrite(dbgName, atlas);
|
|
|
|
// DEBUG_EXTRA("Exported %s", dbgName);
|
|
|
|
// DEBUG_EXTRA("Exported %s", dbgName);
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
atlas = localAtlas; |
|
|
|
} |
|
|
|
} |
|
|
|
DEBUG_EXTRA("Re-rasterization done."); |
|
|
|
DEBUG_EXTRA("Re-rasterization done."); |
|
|
|
|
|
|
|
|
|
|
|
@ -15934,6 +15946,7 @@ 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); // 至少 4096
|
|
|
|
|
|
|
|
|
|
|
|
// 2. atlas + score
|
|
|
|
// 2. atlas + score
|
|
|
|
outTextures.emplace_back(textureSize, textureSize); |
|
|
|
outTextures.emplace_back(textureSize, textureSize); |
|
|
|
@ -17272,10 +17285,11 @@ int MeshTexture::ComputeOptimalTextureSizeAdaptive( |
|
|
|
const std::vector<std::vector<IIndex>>& virtualFaceViews, |
|
|
|
const std::vector<std::vector<IIndex>>& virtualFaceViews, |
|
|
|
unsigned nTextureSizeMultiple) |
|
|
|
unsigned nTextureSizeMultiple) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (virtualFaceMap.empty()) return 2048; |
|
|
|
if (virtualFaceMap.empty()) |
|
|
|
|
|
|
|
return (int)nTextureSizeMultiple; // 没数据就直接用上限
|
|
|
|
|
|
|
|
|
|
|
|
double totalPixels = 0.0; |
|
|
|
double totalPixels = 0.0; |
|
|
|
double totalAreaUV = 0.0; |
|
|
|
int validFaces = 0; |
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < virtualFaceMap.size(); ++i) { |
|
|
|
for (size_t i = 0; i < virtualFaceMap.size(); ++i) { |
|
|
|
const VirtualFace& vf = virtualFaceMap[i]; |
|
|
|
const VirtualFace& vf = virtualFaceMap[i]; |
|
|
|
@ -17288,13 +17302,7 @@ int MeshTexture::ComputeOptimalTextureSizeAdaptive( |
|
|
|
const Image& img = images[viewID]; |
|
|
|
const Image& img = images[viewID]; |
|
|
|
if (img.image.empty()) continue; |
|
|
|
if (img.image.empty()) continue; |
|
|
|
|
|
|
|
|
|
|
|
// UV 面积
|
|
|
|
// 用任意一个面估算投影面积即可
|
|
|
|
const AABB2f& uv = vf.uvBounds; |
|
|
|
|
|
|
|
float uvArea = (uv.ptMax.x() - uv.ptMin.x()) * |
|
|
|
|
|
|
|
(uv.ptMax.y() - uv.ptMin.y()); |
|
|
|
|
|
|
|
if (uvArea <= 0.0f) continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 投影到图像,估算像素覆盖
|
|
|
|
|
|
|
|
const Mesh::Face& face = scene.mesh.faces[vf.faces[0]]; |
|
|
|
const Mesh::Face& face = scene.mesh.faces[vf.faces[0]]; |
|
|
|
const Point3f& v0 = scene.mesh.vertices[face[0]]; |
|
|
|
const Point3f& v0 = scene.mesh.vertices[face[0]]; |
|
|
|
const Point3f& v1 = scene.mesh.vertices[face[1]]; |
|
|
|
const Point3f& v1 = scene.mesh.vertices[face[1]]; |
|
|
|
@ -17304,42 +17312,38 @@ int MeshTexture::ComputeOptimalTextureSizeAdaptive( |
|
|
|
Point2d p1 = img.camera.ProjectPoint(Point3d(v1)); |
|
|
|
Point2d p1 = img.camera.ProjectPoint(Point3d(v1)); |
|
|
|
Point2d p2 = img.camera.ProjectPoint(Point3d(v2)); |
|
|
|
Point2d p2 = img.camera.ProjectPoint(Point3d(v2)); |
|
|
|
|
|
|
|
|
|
|
|
// 图像空间三角形面积(像素)
|
|
|
|
|
|
|
|
double imgArea = |
|
|
|
double imgArea = |
|
|
|
std::abs((p1.x - p0.x) * (p2.y - p0.y) - |
|
|
|
std::abs((p1.x - p0.x) * (p2.y - p0.y) - |
|
|
|
(p2.x - p0.x) * (p1.y - p0.y)) * 0.5; |
|
|
|
(p2.x - p0.x) * (p1.y - p0.y)) * 0.5; |
|
|
|
|
|
|
|
|
|
|
|
if (imgArea < 1.0) continue; |
|
|
|
if (imgArea < 1.0) continue; |
|
|
|
|
|
|
|
|
|
|
|
// 加权累加
|
|
|
|
|
|
|
|
totalPixels += imgArea; |
|
|
|
totalPixels += imgArea; |
|
|
|
totalAreaUV += uvArea; |
|
|
|
++validFaces; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (totalAreaUV <= 0.0) return 2048; |
|
|
|
if (validFaces == 0) |
|
|
|
|
|
|
|
return (int)nTextureSizeMultiple; |
|
|
|
// 核心公式:
|
|
|
|
|
|
|
|
// texelPerUV = sqrt(图像像素总面积 / UV总面积)
|
|
|
|
|
|
|
|
double texelsPerUV = std::sqrt(totalPixels / totalAreaUV); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 用 UV 包围盒大小推算最终纹理尺寸
|
|
|
|
// ★ 关键:目标 1 texel ≈ 1 image pixel
|
|
|
|
AABB2f globalUV(true); |
|
|
|
int textureSize = (int)std::sqrt(totalPixels); |
|
|
|
for (const auto& vf : virtualFaceMap) |
|
|
|
|
|
|
|
globalUV.Insert(vf.uvBounds); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float uvW = globalUV.ptMax.x() - globalUV.ptMin.x(); |
|
|
|
// 可选:你想要更“锐利”可以乘以 1.x(如 1.25)
|
|
|
|
float uvH = globalUV.ptMax.y() - globalUV.ptMin.y(); |
|
|
|
// textureSize = (int)(textureSize * 1.25);
|
|
|
|
|
|
|
|
|
|
|
|
int textureSize = static_cast<int>( |
|
|
|
// 对齐到 2 的幂
|
|
|
|
std::max(uvW, uvH) * texelsPerUV |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 对齐到 2^n,不超过最大限制
|
|
|
|
|
|
|
|
textureSize = RoundUpPowerOfTwo(textureSize); |
|
|
|
textureSize = RoundUpPowerOfTwo(textureSize); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ★ 上限必须是 nTextureSizeMultiple(8192)
|
|
|
|
textureSize = std::min(textureSize, (int)nTextureSizeMultiple); |
|
|
|
textureSize = std::min(textureSize, (int)nTextureSizeMultiple); |
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Adaptive texture size: %d (texels/UV: %.1f)", |
|
|
|
// 防止过小
|
|
|
|
textureSize, texelsPerUV); |
|
|
|
if (textureSize < 1024) textureSize = 1024; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Adaptive texture size: %d (totalPixels=%.0f, validFaces=%d, max=%u)", |
|
|
|
|
|
|
|
textureSize, totalPixels, validFaces, nTextureSizeMultiple); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("totalPixels=%.0f → textureSize=%d", totalPixels, textureSize); |
|
|
|
|
|
|
|
|
|
|
|
return textureSize; |
|
|
|
return textureSize; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -17363,33 +17367,49 @@ bool MeshTexture::SelectBestViewsForVirtualFaces( |
|
|
|
|
|
|
|
|
|
|
|
// ===== 从磁盘加载图像,计算全局平均颜色 =====
|
|
|
|
// ===== 从磁盘加载图像,计算全局平均颜色 =====
|
|
|
|
static bool g_avgColorsComputed = false; |
|
|
|
static bool g_avgColorsComputed = false; |
|
|
|
if (!g_avgColorsComputed) { |
|
|
|
// if (!g_avgColorsComputed) {
|
|
|
|
const size_t numViews = images.size(); // ← 你的变量名
|
|
|
|
// const size_t numViews = images.size(); // ← 你的变量名
|
|
|
|
g_avgColors.resize(numViews, cv::Vec3f(-1, -1, -1)); |
|
|
|
// g_avgColors.resize(numViews, cv::Vec3f(-1, -1, -1));
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t vid = 0; vid < numViews; ++vid) { |
|
|
|
// for (size_t vid = 0; vid < numViews; ++vid) {
|
|
|
|
const std::string& imgPath = images[vid].name; // ← 你的字段名
|
|
|
|
// const std::string& imgPath = images[vid].name; // ← 你的字段名
|
|
|
|
|
|
|
|
|
|
|
|
cv::Mat img = cv::imread(imgPath, cv::IMREAD_COLOR); |
|
|
|
// cv::Mat img = cv::imread(imgPath, cv::IMREAD_COLOR);
|
|
|
|
if (img.empty()) { |
|
|
|
// if (img.empty()) {
|
|
|
|
VERBOSE("[AvgColor] view %zu: cannot load '%s'", vid, imgPath.c_str()); |
|
|
|
// VERBOSE("[AvgColor] view %zu: cannot load '%s'", vid, imgPath.c_str());
|
|
|
|
continue; |
|
|
|
// continue;
|
|
|
|
} |
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// // 直接转 float 算均值,不 resize
|
|
|
|
|
|
|
|
// cv::Mat imgF;
|
|
|
|
|
|
|
|
// img.convertTo(imgF, CV_32FC3, 1.0 / 255.0);
|
|
|
|
|
|
|
|
// cv::Scalar mean = cv::mean(imgF);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// g_avgColors[vid] = cv::Vec3f(mean[0], mean[1], mean[2]);
|
|
|
|
|
|
|
|
|
|
|
|
// 直接转 float 算均值,不 resize
|
|
|
|
// VERBOSE("[AvgColor] view %zu: mean=(%.3f,%.3f,%.3f)",
|
|
|
|
|
|
|
|
// vid, mean[0], mean[1], mean[2]);
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// g_avgColorsComputed = true;
|
|
|
|
|
|
|
|
// VERBOSE("[AvgColor] Done: [0]=(%.3f,%.3f,%.3f)",
|
|
|
|
|
|
|
|
// g_avgColors[0][0], g_avgColors[0][1], g_avgColors[0][2]);
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
g_avgColorsComputed = false; |
|
|
|
|
|
|
|
if (!g_avgColorsComputed) { |
|
|
|
|
|
|
|
g_avgColors.assign(images.size(), cv::Vec3f(-1, -1, -1)); |
|
|
|
|
|
|
|
for (size_t vid = 0; vid < images.size(); ++vid) { |
|
|
|
|
|
|
|
const cv::Mat& img = images[vid].image; // ★ 直接用内存图像
|
|
|
|
|
|
|
|
if (img.empty()) continue; |
|
|
|
cv::Mat imgF; |
|
|
|
cv::Mat imgF; |
|
|
|
img.convertTo(imgF, CV_32FC3, 1.0 / 255.0); |
|
|
|
img.convertTo(imgF, CV_32FC3, 1.0 / 255.0); |
|
|
|
cv::Scalar mean = cv::mean(imgF); |
|
|
|
cv::Scalar mean = cv::mean(imgF); |
|
|
|
|
|
|
|
g_avgColors[vid] = cv::Vec3f(mean[0], mean[1], mean[2]); // BGR
|
|
|
|
g_avgColors[vid] = cv::Vec3f(mean[0], mean[1], mean[2]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VERBOSE("[AvgColor] view %zu: mean=(%.3f,%.3f,%.3f)", |
|
|
|
|
|
|
|
vid, mean[0], mean[1], mean[2]); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
g_avgColorsComputed = true; |
|
|
|
g_avgColorsComputed = true; |
|
|
|
VERBOSE("[AvgColor] Done: [0]=(%.3f,%.3f,%.3f)", |
|
|
|
VERBOSE("[AvgColor] Done: [0]=(%.3f,%.3f,%.3f), [1]=(%.3f,%.3f,%.3f)", |
|
|
|
g_avgColors[0][0], g_avgColors[0][1], g_avgColors[0][2]); |
|
|
|
g_avgColors[0][0], g_avgColors[0][1], g_avgColors[0][2], |
|
|
|
|
|
|
|
g_avgColors[1][0], g_avgColors[1][1], g_avgColors[1][2]); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (g_avgColors.empty()) { |
|
|
|
if (g_avgColors.empty()) { |
|
|
|
@ -17438,6 +17458,30 @@ if (!g_avgColorsComputed) { |
|
|
|
scene.mesh.faceFaces.empty() ? 0xFFFFFFFF : scene.mesh.faceFaces[0][1], |
|
|
|
scene.mesh.faceFaces.empty() ? 0xFFFFFFFF : scene.mesh.faceFaces[0][1], |
|
|
|
scene.mesh.faceFaces.empty() ? 0xFFFFFFFF : scene.mesh.faceFaces[0][2]); |
|
|
|
scene.mesh.faceFaces.empty() ? 0xFFFFFFFF : scene.mesh.faceFaces[0][2]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ★★★ 关键修复:从 faceFaces 构建 faceNeighbors(virtual face 邻接)★★★
|
|
|
|
|
|
|
|
if (faceNeighbors.empty()) { |
|
|
|
|
|
|
|
faceNeighbors.resize(virtualFaceMap.size()); |
|
|
|
|
|
|
|
for (size_t fid = 0; fid < virtualFaceMap.size(); ++fid) { |
|
|
|
|
|
|
|
if (fid >= scene.mesh.faceFaces.size()) break; |
|
|
|
|
|
|
|
const Mesh::FaceFaces& ff = scene.mesh.faceFaces[fid]; // 原始面邻接
|
|
|
|
|
|
|
|
for (int k = 0; k < 3; ++k) { |
|
|
|
|
|
|
|
FIndex nb = ff[k]; |
|
|
|
|
|
|
|
if (nb == NO_ID || nb >= (FIndex)virtualFaceMap.size()) continue; |
|
|
|
|
|
|
|
// 去重插入
|
|
|
|
|
|
|
|
bool exists = false; |
|
|
|
|
|
|
|
for (IIndex existing : faceNeighbors[fid]) { |
|
|
|
|
|
|
|
if ((FIndex)existing == nb) { exists = true; break; } |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (!exists) faceNeighbors[fid].push_back((IIndex)nb); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
DEBUG_EXTRA("[SelectBest] faceNeighbors built: size=%zu, avg=%f", |
|
|
|
|
|
|
|
faceNeighbors.size(), |
|
|
|
|
|
|
|
faceNeighbors.size() > 0 ? |
|
|
|
|
|
|
|
(double)std::accumulate(faceNeighbors.begin(), faceNeighbors.end(), 0ull, |
|
|
|
|
|
|
|
[](size_t s, const auto& v) { return s + v.size(); }) / faceNeighbors.size() : 0); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ---------- 1. 初始视图分配 ----------
|
|
|
|
// ---------- 1. 初始视图分配 ----------
|
|
|
|
size_t emptyCandidateViews = 0; |
|
|
|
size_t emptyCandidateViews = 0; |
|
|
|
size_t fallbackByCenterFace = 0; |
|
|
|
size_t fallbackByCenterFace = 0; |
|
|
|
@ -17525,7 +17569,7 @@ if (!g_avgColorsComputed) { |
|
|
|
|
|
|
|
|
|
|
|
// ========== 颜色一致性代价 ==========
|
|
|
|
// ========== 颜色一致性代价 ==========
|
|
|
|
{ |
|
|
|
{ |
|
|
|
const float lambda = 0.0f; |
|
|
|
const float lambda = 2.0f; |
|
|
|
// ========== 收集邻居颜色(修复版:1-ring + faceToView)==========
|
|
|
|
// ========== 收集邻居颜色(修复版:1-ring + faceToView)==========
|
|
|
|
std::vector<cv::Vec3f> neighborColors; |
|
|
|
std::vector<cv::Vec3f> neighborColors; |
|
|
|
if (!scene.mesh.faceFaces.empty() && faceID < scene.mesh.faceFaces.size()) { |
|
|
|
if (!scene.mesh.faceFaces.empty() && faceID < scene.mesh.faceFaces.size()) { |
|
|
|
@ -17574,7 +17618,8 @@ if (!g_avgColorsComputed) { |
|
|
|
cv::Vec3f d = candidateColor - nc; |
|
|
|
cv::Vec3f d = candidateColor - nc; |
|
|
|
colorDiff += std::sqrt(d[0]*d[0] + d[1]*d[1] + d[2]*d[2]); |
|
|
|
colorDiff += std::sqrt(d[0]*d[0] + d[1]*d[1] + d[2]*d[2]); |
|
|
|
} |
|
|
|
} |
|
|
|
colorDiff = (colorDiff / neighborColors.size()) / 1.732f; |
|
|
|
// colorDiff = (colorDiff / neighborColors.size()) / 1.732f;
|
|
|
|
|
|
|
|
colorDiff = colorDiff / neighborColors.size(); |
|
|
|
colorDiff = std::min(colorDiff, 1.0f); |
|
|
|
colorDiff = std::min(colorDiff, 1.0f); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 修复:不再乘 score,避免放大效应
|
|
|
|
// ✅ 修复:不再乘 score,避免放大效应
|
|
|
|
@ -21916,6 +21961,7 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi |
|
|
|
const std::string& inputFileName, const std::string& meshFileName, bool bUseExistingUV, const std::string& strUVMeshFileName) |
|
|
|
const std::string& inputFileName, const std::string& meshFileName, bool bUseExistingUV, const std::string& strUVMeshFileName) |
|
|
|
{ |
|
|
|
{ |
|
|
|
nTextureSizeMultiple = 8192; // 8192 4096
|
|
|
|
nTextureSizeMultiple = 8192; // 8192 4096
|
|
|
|
|
|
|
|
maxTextureSize = 8192; // ← 加这行
|
|
|
|
|
|
|
|
|
|
|
|
// 预处理:计算网格拓扑和几何信息
|
|
|
|
// 预处理:计算网格拓扑和几何信息
|
|
|
|
if (!bOriginFaceview) |
|
|
|
if (!bOriginFaceview) |
|
|
|
@ -22022,11 +22068,29 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi |
|
|
|
|
|
|
|
|
|
|
|
texture.EstimateGlobalPhotometricCorrection(); |
|
|
|
texture.EstimateGlobalPhotometricCorrection(); |
|
|
|
|
|
|
|
|
|
|
|
// // ✅ 2. 计算可见性(填充 faceNeighbors)
|
|
|
|
if (!texture.s_gainsApplied && texture.m_gainsEstimated && !texture.m_imageGains.empty()) { |
|
|
|
// if (!texture.ComputePureFaceVisibility(
|
|
|
|
for (size_t vid = 0; vid < images.size(); ++vid) { |
|
|
|
// fOutlierThreshold, nIgnoreMaskLabel, views)) {
|
|
|
|
if (vid >= texture.m_imageGains.size()) break; |
|
|
|
// return false;
|
|
|
|
const cv::Vec3f& gain = texture.m_imageGains[vid]; |
|
|
|
// }
|
|
|
|
cv::Mat& img = images[vid].image; |
|
|
|
|
|
|
|
for (int r = 0; r < img.rows; ++r) { |
|
|
|
|
|
|
|
for (int c = 0; c < img.cols; ++c) { |
|
|
|
|
|
|
|
cv::Vec3b& p = img.at<cv::Vec3b>(r, c); |
|
|
|
|
|
|
|
p[0] = cv::saturate_cast<uint8_t>(p[0] * gain[0]); |
|
|
|
|
|
|
|
p[1] = cv::saturate_cast<uint8_t>(p[1] * gain[1]); |
|
|
|
|
|
|
|
p[2] = cv::saturate_cast<uint8_t>(p[2] * gain[2]); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
texture.s_gainsApplied = true; |
|
|
|
|
|
|
|
DEBUG_EXTRA("Photometric gains applied to %zu images", images.size()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ 2. 计算可见性(填充 faceNeighbors)
|
|
|
|
|
|
|
|
if (!texture.ComputePureFaceVisibility( |
|
|
|
|
|
|
|
fOutlierThreshold, nIgnoreMaskLabel, views)) { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 3. 选择最佳视图(带 patch 一致性)
|
|
|
|
// ✅ 3. 选择最佳视图(带 patch 一致性)
|
|
|
|
if (!texture.SelectBestViewsForVirtualFaces( |
|
|
|
if (!texture.SelectBestViewsForVirtualFaces( |
|
|
|
|