|
|
|
|
@ -10231,7 +10231,92 @@ bool MeshTexture::PackTextureAtlases(
@@ -10231,7 +10231,92 @@ bool MeshTexture::PackTextureAtlases(
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
cv::Rect MeshTexture::ComputeOptimalPatchBounds(const AABB2f& aabb, const cv::Size& imageSize, int border) { |
|
|
|
|
DEBUG_EXTRA("Computing optimal patch bounds for AABB, image: %dx%d, border: %d", |
|
|
|
|
imageSize.width, imageSize.height, border); |
|
|
|
|
|
|
|
|
|
// 检查输入参数的有效性
|
|
|
|
|
if (imageSize.width <= 0 || imageSize.height <= 0) { |
|
|
|
|
DEBUG_EXTRA("Error: Invalid image size: %dx%d", imageSize.width, imageSize.height); |
|
|
|
|
return cv::Rect(0, 0, 0, 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (border < 0) { |
|
|
|
|
border = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 步骤1: 获取AABB的边界值
|
|
|
|
|
float x1, y1, x2, y2; |
|
|
|
|
|
|
|
|
|
// 确保aabb是有效的
|
|
|
|
|
if (aabb.ptMin.x() >= aabb.ptMax.x() || aabb.ptMin.y() >= aabb.ptMax.y()) { |
|
|
|
|
DEBUG_EXTRA("Error: Invalid AABB"); |
|
|
|
|
return cv::Rect(0, 0, 0, 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
x1 = aabb.ptMin.x(); |
|
|
|
|
y1 = aabb.ptMin.y(); |
|
|
|
|
x2 = aabb.ptMax.x(); |
|
|
|
|
y2 = aabb.ptMax.y(); |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("AABB bounds: [%f, %f] - [%f, %f]", x1, y1, x2, y2); |
|
|
|
|
|
|
|
|
|
// 步骤2: 计算基本边界(包含边距)
|
|
|
|
|
float minX = x1 - border; |
|
|
|
|
float minY = y1 - border; |
|
|
|
|
float maxX = x2 + border; |
|
|
|
|
float maxY = y2 + border; |
|
|
|
|
|
|
|
|
|
// 步骤3: 确保边界不超出图像范围
|
|
|
|
|
minX = std::max(0.0f, minX); |
|
|
|
|
minY = std::max(0.0f, minY); |
|
|
|
|
maxX = std::min(static_cast<float>(imageSize.width) - 1.0f, maxX); |
|
|
|
|
maxY = std::min(static_cast<float>(imageSize.height) - 1.0f, maxY); |
|
|
|
|
|
|
|
|
|
// 检查边界是否有效
|
|
|
|
|
if (minX >= maxX || minY >= maxY) { |
|
|
|
|
DEBUG_EXTRA("Warning: Invalid bounds after clamping: [%f, %f] - [%f, %f]", |
|
|
|
|
minX, minY, maxX, maxY); |
|
|
|
|
// 返回一个最小边界
|
|
|
|
|
int x = static_cast<int>(std::floor(x1)); |
|
|
|
|
int y = static_cast<int>(std::floor(y1)); |
|
|
|
|
x = std::max(0, std::min(x, imageSize.width - 1)); |
|
|
|
|
y = std::max(0, std::min(y, imageSize.height - 1)); |
|
|
|
|
return cv::Rect(x, y, 1, 1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 步骤4: 对齐到整数像素坐标
|
|
|
|
|
int x = static_cast<int>(std::floor(minX)); |
|
|
|
|
int y = static_cast<int>(std::floor(minY)); |
|
|
|
|
int width = static_cast<int>(std::ceil(maxX)) - x; |
|
|
|
|
int height = static_cast<int>(std::ceil(maxY)) - y; |
|
|
|
|
|
|
|
|
|
// 确保最小尺寸
|
|
|
|
|
width = std::max(1, width); |
|
|
|
|
height = std::max(1, height); |
|
|
|
|
|
|
|
|
|
// 确保不超出图像范围
|
|
|
|
|
if (x < 0) x = 0; |
|
|
|
|
if (y < 0) y = 0; |
|
|
|
|
if (x >= imageSize.width) x = imageSize.width - 1; |
|
|
|
|
if (y >= imageSize.height) y = imageSize.height - 1; |
|
|
|
|
if (x + width > imageSize.width) { |
|
|
|
|
width = imageSize.width - x; |
|
|
|
|
} |
|
|
|
|
if (y + height > imageSize.height) { |
|
|
|
|
height = imageSize.height - y; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 最终验证
|
|
|
|
|
width = std::max(1, width); |
|
|
|
|
height = std::max(1, height); |
|
|
|
|
|
|
|
|
|
cv::Rect result(x, y, width, height); |
|
|
|
|
DEBUG_EXTRA("Optimal patch bounds computed: [%d, %d, %d, %d]", |
|
|
|
|
result.x, result.y, result.width, result.height); |
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
// 主要改进的纹理映射函数
|
|
|
|
|
bool MeshTexture::GenerateTextureWithViewConsistency( |
|
|
|
|
bool bGlobalSeamLeveling, bool bLocalSeamLeveling, |
|
|
|
|
@ -10243,55 +10328,149 @@ bool MeshTexture::GenerateTextureWithViewConsistency(
@@ -10243,55 +10328,149 @@ bool MeshTexture::GenerateTextureWithViewConsistency(
|
|
|
|
|
TD_TIMER_START(); |
|
|
|
|
|
|
|
|
|
const int border = (bOriginFaceview) ? 2 : 4; |
|
|
|
|
const int minPatchSize = 50; // 最小纹理块大小
|
|
|
|
|
const int minPatchSize = 50; |
|
|
|
|
|
|
|
|
|
// 1. 创建视图一致性映射
|
|
|
|
|
std::vector<ViewSelectionData> faceViewData(faces.size()); |
|
|
|
|
std::vector<int> patchAssignments(faces.size(), -1); |
|
|
|
|
|
|
|
|
|
// 2. 为每个面选择最佳视图(考虑相邻面一致性)
|
|
|
|
|
// 2. 为每个面选择最佳视图
|
|
|
|
|
SelectOptimalViewsWithConsistency(faceViewData, minPatchSize); |
|
|
|
|
|
|
|
|
|
// 3. 基于视图一致性创建纹理块
|
|
|
|
|
CreateConsistentTexturePatches(faceViewData, patchAssignments, minPatchSize); |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Created %zu texture patches", texturePatches.size()); |
|
|
|
|
|
|
|
|
|
// 检查纹理块是否有效
|
|
|
|
|
if (texturePatches.IsEmpty()) { |
|
|
|
|
DEBUG_EXTRA("Error: No texture patches created"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 4. 生成纹理坐标
|
|
|
|
|
Mesh::TexCoordArr faceTexcoords2(faces.size() * 3); |
|
|
|
|
Mesh::TexIndexArr faceTexindices2(faces.size()); |
|
|
|
|
|
|
|
|
|
#ifdef TEXOPT_USE_OPENMP |
|
|
|
|
#pragma omp parallel for schedule(dynamic) |
|
|
|
|
for (int_t idx = 0; idx < (int_t)texturePatches.size() - 1; ++idx) { |
|
|
|
|
#else |
|
|
|
|
for (uint32_t idx = 0; idx < texturePatches.size() - 1; ++idx) { |
|
|
|
|
#endif |
|
|
|
|
DEBUG_EXTRA("Processing %zu texture patches", texturePatches.size()); |
|
|
|
|
|
|
|
|
|
// 计算需要处理的纹理块数量
|
|
|
|
|
size_t numPatchesToProcess = texturePatches.size(); |
|
|
|
|
if (!texturePatches.empty() && texturePatches.back().label == NO_ID) { |
|
|
|
|
numPatchesToProcess = texturePatches.size() - 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Processing %zu valid texture patches", numPatchesToProcess); |
|
|
|
|
|
|
|
|
|
// 处理有效纹理块
|
|
|
|
|
for (size_t idx = 0; idx < numPatchesToProcess; ++idx) { |
|
|
|
|
TexturePatch& texturePatch = texturePatches[idx]; |
|
|
|
|
|
|
|
|
|
// 检查纹理块是否有效
|
|
|
|
|
if (texturePatch.faces.IsEmpty()) { |
|
|
|
|
DEBUG_EXTRA("Warning: Texture patch %zu is empty", idx); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 检查视图ID是否有效
|
|
|
|
|
if (texturePatch.label < 0 || texturePatch.label >= (int)images.size()) { |
|
|
|
|
DEBUG_EXTRA("Warning: Texture patch %zu has invalid label: %d", idx, texturePatch.label); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const Image& imageData = images[texturePatch.label]; |
|
|
|
|
|
|
|
|
|
// 检查图像是否有效
|
|
|
|
|
if (imageData.image.empty()) { |
|
|
|
|
DEBUG_EXTRA("Warning: Image for patch %zu is empty", idx); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
AABB2f aabb(true); |
|
|
|
|
|
|
|
|
|
// 计算纹理块的UV边界
|
|
|
|
|
bool validAABB = false; |
|
|
|
|
for (const FIndex idxFace : texturePatch.faces) { |
|
|
|
|
if (idxFace >= faces.size()) { |
|
|
|
|
DEBUG_EXTRA("Warning: Invalid face index in patch %zu: %u", idx, idxFace); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const Face& face = faces[idxFace]; |
|
|
|
|
TexCoord* texcoords = faceTexcoords2.data() + idxFace * 3; |
|
|
|
|
|
|
|
|
|
bool faceValid = true; |
|
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
|
if (face[i] >= vertices.size()) { |
|
|
|
|
DEBUG_EXTRA("Warning: Invalid vertex index in face %u", idxFace); |
|
|
|
|
faceValid = false; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
texcoords[i] = imageData.camera.ProjectPointP(vertices[face[i]]); |
|
|
|
|
ASSERT(imageData.image.isInsideWithBorder(texcoords[i], border)); |
|
|
|
|
|
|
|
|
|
// 检查纹理坐标是否在图像边界内
|
|
|
|
|
if (!imageData.image.isInsideWithBorder(texcoords[i], border)) { |
|
|
|
|
float border_f = static_cast<float>(border); |
|
|
|
|
float imgWidth = static_cast<float>(imageData.image.width()); |
|
|
|
|
float imgHeight = static_cast<float>(imageData.image.height()); |
|
|
|
|
|
|
|
|
|
texcoords[i].x = std::max(border_f, std::min(texcoords[i].x, imgWidth - border_f - 1.0f)); |
|
|
|
|
texcoords[i].y = std::max(border_f, std::min(texcoords[i].y, imgHeight - border_f - 1.0f)); |
|
|
|
|
} |
|
|
|
|
aabb.InsertFull(texcoords[i]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (faceValid) { |
|
|
|
|
validAABB = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!validAABB) { |
|
|
|
|
DEBUG_EXTRA("Warning: Texture patch %zu has no valid faces", idx); |
|
|
|
|
texturePatch.rect = cv::Rect(0, 0, 1, 1); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 设置纹理块边界
|
|
|
|
|
texturePatch.rect = ComputeOptimalPatchBounds(aabb, imageData.image.size(), border); |
|
|
|
|
cv::Rect patchRect = ComputeOptimalPatchBounds(aabb, imageData.image.size(), border); |
|
|
|
|
|
|
|
|
|
// 检查边界是否有效
|
|
|
|
|
if (patchRect.width <= 0 || patchRect.height <= 0 || |
|
|
|
|
patchRect.x < 0 || patchRect.y < 0 || |
|
|
|
|
patchRect.x + patchRect.width > imageData.image.width() || |
|
|
|
|
patchRect.y + patchRect.height > imageData.image.height()) { |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Warning: Invalid rect for patch %zu: [%d, %d, %d, %d]", idx, |
|
|
|
|
patchRect.x, patchRect.y, patchRect.width, patchRect.height); |
|
|
|
|
|
|
|
|
|
// 设置一个安全的边界
|
|
|
|
|
int safeX = std::max(0, patchRect.x); |
|
|
|
|
int safeY = std::max(0, patchRect.y); |
|
|
|
|
int safeWidth = std::max(1, std::min(imageData.image.width() - safeX, patchRect.width)); |
|
|
|
|
int safeHeight = std::max(1, std::min(imageData.image.height() - safeY, patchRect.height)); |
|
|
|
|
|
|
|
|
|
patchRect = cv::Rect(safeX, safeY, safeWidth, safeHeight); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
texturePatch.rect = patchRect; |
|
|
|
|
DEBUG_EXTRA("Patch %zu bounds: [%d, %d, %d, %d]", idx, |
|
|
|
|
patchRect.x, patchRect.y, patchRect.width, patchRect.height); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 5. 处理剩余的"无效视图"面(分配到默认纹理块)
|
|
|
|
|
// 5. 处理无效视图纹理块
|
|
|
|
|
if (!texturePatches.empty() && texturePatches.back().label == NO_ID) { |
|
|
|
|
DEBUG_EXTRA("Processing invalid view patch"); |
|
|
|
|
TexturePatch& texturePatch = texturePatches.back(); |
|
|
|
|
const int sizePatch = border * 2 + 1; |
|
|
|
|
texturePatch.rect = cv::Rect(0, 0, sizePatch, sizePatch); |
|
|
|
|
|
|
|
|
|
for (const FIndex idxFace : texturePatch.faces) { |
|
|
|
|
if (idxFace >= faces.size()) { |
|
|
|
|
DEBUG_EXTRA("Warning: Invalid face index in invalid patch: %u", idxFace); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TexCoord* texcoords = faceTexcoords2.data() + idxFace * 3; |
|
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
|
texcoords[i] = TexCoord(0.5f, 0.5f); |
|
|
|
|
@ -10301,39 +10480,45 @@ bool MeshTexture::GenerateTextureWithViewConsistency(
@@ -10301,39 +10480,45 @@ bool MeshTexture::GenerateTextureWithViewConsistency(
|
|
|
|
|
|
|
|
|
|
// 6. 执行接缝均衡
|
|
|
|
|
if (texturePatches.size() > 2) { |
|
|
|
|
DEBUG_EXTRA("Creating seam vertices"); |
|
|
|
|
CreateSeamVertices(); |
|
|
|
|
|
|
|
|
|
if (bGlobalSeamLeveling) { |
|
|
|
|
DEBUG_EXTRA("Starting global seam leveling"); |
|
|
|
|
GlobalSeamLevelingEnhanced(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (bLocalSeamLeveling) { |
|
|
|
|
DEBUG_EXTRA("Starting local seam leveling"); |
|
|
|
|
LocalSeamLevelingEnhanced(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 7. 合并重叠的纹理块
|
|
|
|
|
DEBUG_EXTRA("Merging overlapping patches"); |
|
|
|
|
MergeOverlappingPatches(faceTexcoords2); |
|
|
|
|
|
|
|
|
|
// 8. 打包纹理块
|
|
|
|
|
DEBUG_EXTRA("Packing texture atlases"); |
|
|
|
|
std::vector<Image8U3> generatedTextures; |
|
|
|
|
|
|
|
|
|
// 修正:使用正确的函数名和参数
|
|
|
|
|
// 在 OpenMVS 中,纹理打包通常使用 PackTextureAtlases
|
|
|
|
|
PackTextureAtlases(faceTexcoords2, faceTexindices2, generatedTextures, |
|
|
|
|
nTextureSizeMultiple, nRectPackingHeuristic, |
|
|
|
|
colEmpty, maxTextureSize); |
|
|
|
|
|
|
|
|
|
// 9. 高质量纹理采样
|
|
|
|
|
DEBUG_EXTRA("Generating high-quality texture"); |
|
|
|
|
GenerateHighQualityTexture(generatedTextures, faceTexcoords2, faceTexindices2, |
|
|
|
|
fSharpnessWeight, colEmpty); |
|
|
|
|
|
|
|
|
|
// 10. 应用纹理锐化
|
|
|
|
|
if (fSharpnessWeight > 0) { |
|
|
|
|
DEBUG_EXTRA("Applying adaptive sharpening"); |
|
|
|
|
ApplyAdaptiveSharpening(generatedTextures, fSharpnessWeight); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 11. 填充空洞
|
|
|
|
|
DEBUG_EXTRA("Filling texture holes"); |
|
|
|
|
FillTextureHoles(generatedTextures, colEmpty); |
|
|
|
|
|
|
|
|
|
// 12. 保存结果
|
|
|
|
|
@ -10565,7 +10750,6 @@ void MeshTexture::SelectOptimalViewsWithConsistency(
@@ -10565,7 +10750,6 @@ void MeshTexture::SelectOptimalViewsWithConsistency(
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("View selection with consistency completed"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 创建一致性纹理块
|
|
|
|
|
void MeshTexture::CreateConsistentTexturePatches( |
|
|
|
|
const std::vector<ViewSelectionData>& faceViewData, |
|
|
|
|
@ -10574,10 +10758,28 @@ void MeshTexture::CreateConsistentTexturePatches(
@@ -10574,10 +10758,28 @@ void MeshTexture::CreateConsistentTexturePatches(
|
|
|
|
|
{ |
|
|
|
|
DEBUG_EXTRA("Creating consistent texture patches"); |
|
|
|
|
|
|
|
|
|
const int numFaces = faces.size(); |
|
|
|
|
const int numFaces = static_cast<int>(faces.size()); |
|
|
|
|
std::vector<bool> visited(numFaces, false); |
|
|
|
|
int patchCounter = 0; |
|
|
|
|
|
|
|
|
|
// 清空现有的纹理块
|
|
|
|
|
texturePatches.clear(); |
|
|
|
|
|
|
|
|
|
// 步骤0: 构建边到面的映射
|
|
|
|
|
std::unordered_map<uint64_t, std::vector<FIndex>> edgeToFaceMap; |
|
|
|
|
for (int fid = 0; fid < numFaces; ++fid) { |
|
|
|
|
const Face& face = faces[fid]; |
|
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
|
VIndex v0 = face[i]; |
|
|
|
|
VIndex v1 = face[(i + 1) % 3]; |
|
|
|
|
|
|
|
|
|
if (v0 > v1) std::swap(v0, v1); |
|
|
|
|
|
|
|
|
|
uint64_t edgeKey = (static_cast<uint64_t>(v0) << 32) | v1; |
|
|
|
|
edgeToFaceMap[edgeKey].push_back(static_cast<FIndex>(fid)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 步骤1: 创建基于视图的纹理块
|
|
|
|
|
std::vector<std::vector<FIndex>> patchFaces; |
|
|
|
|
|
|
|
|
|
@ -10597,24 +10799,29 @@ void MeshTexture::CreateConsistentTexturePatches(
@@ -10597,24 +10799,29 @@ void MeshTexture::CreateConsistentTexturePatches(
|
|
|
|
|
int fid = faceQueue.front(); |
|
|
|
|
faceQueue.pop(); |
|
|
|
|
|
|
|
|
|
currentPatch.push_back(fid); |
|
|
|
|
currentPatch.push_back(static_cast<FIndex>(fid)); |
|
|
|
|
patchAssignments[fid] = patchCounter; |
|
|
|
|
|
|
|
|
|
const Face& face = faces[fid]; |
|
|
|
|
|
|
|
|
|
// 查找相同视图的相邻面
|
|
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
|
const VIndex idxV0 = face[i]; |
|
|
|
|
const VIndex idxV1 = face[(i + 1) % 3]; |
|
|
|
|
const Edge edge = mesh.EdgeFromVertices(idxV0, idxV1); |
|
|
|
|
VIndex idxV0 = face[i]; |
|
|
|
|
VIndex idxV1 = face[(i + 1) % 3]; |
|
|
|
|
|
|
|
|
|
if (idxV0 > idxV1) std::swap(idxV0, idxV1); |
|
|
|
|
uint64_t edgeKey = (static_cast<uint64_t>(idxV0) << 32) | idxV1; |
|
|
|
|
|
|
|
|
|
auto it = edgeToFaceMap.find(edgeKey); |
|
|
|
|
if (it != edgeToFaceMap.end()) { |
|
|
|
|
for (FIndex neighborFid : it->second) { |
|
|
|
|
int nfid = static_cast<int>(neighborFid); |
|
|
|
|
if (nfid == fid) continue; |
|
|
|
|
if (visited[nfid]) continue; |
|
|
|
|
|
|
|
|
|
if (edge != NO_ID) { |
|
|
|
|
const FaceIdxArr& facePairs = mesh.edgeFaces[edge]; |
|
|
|
|
for (FIndex neighborFid : facePairs) { |
|
|
|
|
if (!visited[neighborFid] && |
|
|
|
|
faceViewData[neighborFid].viewID == viewID) { |
|
|
|
|
visited[neighborFid] = true; |
|
|
|
|
faceQueue.push(neighborFid); |
|
|
|
|
if (faceViewData[nfid].viewID == viewID) { |
|
|
|
|
visited[nfid] = true; |
|
|
|
|
faceQueue.push(nfid); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -10622,18 +10829,20 @@ void MeshTexture::CreateConsistentTexturePatches(
@@ -10622,18 +10829,20 @@ void MeshTexture::CreateConsistentTexturePatches(
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 只保留足够大的纹理块
|
|
|
|
|
if (currentPatch.size() >= minPatchSize) { |
|
|
|
|
if (static_cast<int>(currentPatch.size()) >= minPatchSize) { |
|
|
|
|
patchFaces.push_back(std::move(currentPatch)); |
|
|
|
|
patchCounter++; |
|
|
|
|
} else { |
|
|
|
|
// 小纹理块重新标记为未分配
|
|
|
|
|
for (FIndex fid : currentPatch) { |
|
|
|
|
patchAssignments[fid] = -1; |
|
|
|
|
patchAssignments[static_cast<int>(fid)] = -1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 步骤2: 处理未分配的面(分配到最近的纹理块)
|
|
|
|
|
DEBUG_EXTRA("Initial patch creation: %zu patches", patchFaces.size()); |
|
|
|
|
|
|
|
|
|
// 步骤2: 处理未分配的面
|
|
|
|
|
std::vector<int> unassignedFaces; |
|
|
|
|
for (int fid = 0; fid < numFaces; ++fid) { |
|
|
|
|
if (patchAssignments[fid] == -1 && faceViewData[fid].viewID != -1) { |
|
|
|
|
@ -10641,43 +10850,100 @@ void MeshTexture::CreateConsistentTexturePatches(
@@ -10641,43 +10850,100 @@ void MeshTexture::CreateConsistentTexturePatches(
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#pragma omp parallel for schedule(dynamic) |
|
|
|
|
for (int idx = 0; idx < unassignedFaces.size(); ++idx) { |
|
|
|
|
int fid = unassignedFaces[idx]; |
|
|
|
|
int bestPatch = -1; |
|
|
|
|
float bestDistance = FLT_MAX; |
|
|
|
|
DEBUG_EXTRA("Found %zu unassigned faces", unassignedFaces.size()); |
|
|
|
|
|
|
|
|
|
if (!unassignedFaces.empty()) { |
|
|
|
|
// 构建相邻面映射
|
|
|
|
|
std::vector<std::vector<int>> faceNeighbors(numFaces); |
|
|
|
|
for (int fid = 0; fid < numFaces; ++fid) { |
|
|
|
|
const Face& face = faces[fid]; |
|
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
|
VIndex idxV0 = face[i]; |
|
|
|
|
VIndex idxV1 = face[(i + 1) % 3]; |
|
|
|
|
|
|
|
|
|
if (idxV0 > idxV1) std::swap(idxV0, idxV1); |
|
|
|
|
uint64_t edgeKey = (static_cast<uint64_t>(idxV0) << 32) | idxV1; |
|
|
|
|
|
|
|
|
|
auto it = edgeToFaceMap.find(edgeKey); |
|
|
|
|
if (it != edgeToFaceMap.end()) { |
|
|
|
|
for (FIndex neighborFid : it->second) { |
|
|
|
|
int nfid = static_cast<int>(neighborFid); |
|
|
|
|
if (nfid != fid) { |
|
|
|
|
faceNeighbors[fid].push_back(nfid); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 为未分配的面找到最近的纹理块
|
|
|
|
|
std::vector<int> faceDistances(numFaces, -1); |
|
|
|
|
std::queue<int> bfsQueue; |
|
|
|
|
|
|
|
|
|
// 初始化BFS
|
|
|
|
|
for (int fid = 0; fid < numFaces; ++fid) { |
|
|
|
|
if (patchAssignments[fid] != -1) { |
|
|
|
|
bfsQueue.push(fid); |
|
|
|
|
faceDistances[fid] = 0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 执行BFS
|
|
|
|
|
std::vector<std::pair<int, int>> assignments; |
|
|
|
|
|
|
|
|
|
for (int patchID = 0; patchID < patchFaces.size(); ++patchID) { |
|
|
|
|
if (patchFaces[patchID].empty()) continue; |
|
|
|
|
while (!bfsQueue.empty()) { |
|
|
|
|
int fid = bfsQueue.front(); |
|
|
|
|
bfsQueue.pop(); |
|
|
|
|
|
|
|
|
|
// 查找最近的已分配面
|
|
|
|
|
for (FIndex assignedFid : patchFaces[patchID]) { |
|
|
|
|
float distance = ComputeFaceDistance(fid, assignedFid); |
|
|
|
|
if (distance < bestDistance) { |
|
|
|
|
bestDistance = distance; |
|
|
|
|
bestPatch = patchID; |
|
|
|
|
int currentPatch = patchAssignments[fid]; |
|
|
|
|
int currentDist = faceDistances[fid]; |
|
|
|
|
|
|
|
|
|
for (int neighborFid : faceNeighbors[fid]) { |
|
|
|
|
if (faceDistances[neighborFid] == -1) { |
|
|
|
|
faceDistances[neighborFid] = currentDist + 1; |
|
|
|
|
bfsQueue.push(neighborFid); |
|
|
|
|
|
|
|
|
|
if (patchAssignments[neighborFid] == -1) { |
|
|
|
|
assignments.push_back(std::make_pair(neighborFid, currentPatch)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (bestPatch != -1) { |
|
|
|
|
patchAssignments[fid] = bestPatch; |
|
|
|
|
patchFaces[bestPatch].push_back(fid); |
|
|
|
|
// 处理分配
|
|
|
|
|
for (const auto& assignment : assignments) { |
|
|
|
|
int fid = assignment.first; |
|
|
|
|
int patchID = assignment.second; |
|
|
|
|
|
|
|
|
|
patchAssignments[fid] = patchID; |
|
|
|
|
patchFaces[patchID].push_back(static_cast<FIndex>(fid)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 步骤3: 创建纹理块
|
|
|
|
|
texturePatches.Resize(patchFaces.size() + 1); // +1 用于无效面
|
|
|
|
|
|
|
|
|
|
#pragma omp parallel for schedule(static) |
|
|
|
|
for (int patchID = 0; patchID < patchFaces.size(); ++patchID) { |
|
|
|
|
DEBUG_EXTRA("Allocated %zu texture patches", texturePatches.size()); |
|
|
|
|
|
|
|
|
|
for (size_t patchID = 0; patchID < patchFaces.size(); ++patchID) { |
|
|
|
|
TexturePatch& patch = texturePatches[patchID]; |
|
|
|
|
patch.faces = patchFaces[patchID]; |
|
|
|
|
|
|
|
|
|
// 确定纹理块的视图(取最常见的视图)
|
|
|
|
|
// 检查patchFaces是否为空
|
|
|
|
|
if (patchFaces[patchID].empty()) { |
|
|
|
|
DEBUG_EXTRA("Warning: Patch %zu is empty", patchID); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 复制面索引
|
|
|
|
|
patch.faces.Resize(patchFaces[patchID].size()); |
|
|
|
|
for (size_t i = 0; i < patchFaces[patchID].size(); ++i) { |
|
|
|
|
patch.faces[i] = patchFaces[patchID][i]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 确定纹理块的视图
|
|
|
|
|
std::unordered_map<int, int> viewCounts; |
|
|
|
|
for (FIndex fid : patch.faces) { |
|
|
|
|
int viewID = faceViewData[fid].viewID; |
|
|
|
|
int viewID = faceViewData[static_cast<int>(fid)].viewID; |
|
|
|
|
if (viewID != -1) { |
|
|
|
|
viewCounts[viewID]++; |
|
|
|
|
} |
|
|
|
|
@ -10693,16 +10959,26 @@ void MeshTexture::CreateConsistentTexturePatches(
@@ -10693,16 +10959,26 @@ void MeshTexture::CreateConsistentTexturePatches(
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
patch.label = dominantView; |
|
|
|
|
DEBUG_EXTRA("Patch %zu: %zu faces, label: %d", |
|
|
|
|
patchID, patch.faces.size(), patch.label); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 步骤4: 创建无效面纹理块
|
|
|
|
|
if (!texturePatches.empty()) { |
|
|
|
|
TexturePatch& invalidPatch = texturePatches.back(); |
|
|
|
|
invalidPatch.label = NO_ID; |
|
|
|
|
invalidPatch.faces.Resize(0); |
|
|
|
|
|
|
|
|
|
for (int fid = 0; fid < numFaces; ++fid) { |
|
|
|
|
if (faceViewData[fid].viewID == -1) { |
|
|
|
|
invalidPatch.faces.Insert(fid); |
|
|
|
|
invalidPatch.faces.Insert(static_cast<FIndex>(fid)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Invalid patch: %zu faces", invalidPatch.faces.size()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Created %zu texture patches", texturePatches.size()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 高质量纹理生成
|
|
|
|
|
@ -11006,7 +11282,7 @@ float MeshTexture::ComputeFaceDistance(FIndex fid1, FIndex fid2)
@@ -11006,7 +11282,7 @@ float MeshTexture::ComputeFaceDistance(FIndex fid1, FIndex fid2)
|
|
|
|
|
Point3f diff = center2 - center1; |
|
|
|
|
return std::sqrt(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 判断面是否在视图中可见
|
|
|
|
|
// 判断面是否在视图中可见
|
|
|
|
|
bool MeshTexture::IsFaceVisibleFromView(FIndex idxFace, int viewID) |
|
|
|
|
{ |
|
|
|
|
@ -11021,8 +11297,15 @@ bool MeshTexture::IsFaceVisibleFromView(FIndex idxFace, int viewID)
@@ -11021,8 +11297,15 @@ bool MeshTexture::IsFaceVisibleFromView(FIndex idxFace, int viewID)
|
|
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
|
const cv::Point3f& vertex = vertices[face[i]]; |
|
|
|
|
|
|
|
|
|
// 投影到图像平面
|
|
|
|
|
Point2f proj = imageData.camera.ProjectPointP(vertex); |
|
|
|
|
// 将cv::Point3f转换为OpenMVS的Point3类型
|
|
|
|
|
// Camera类使用double精度,但ProjectPointP是模板函数,支持多种类型
|
|
|
|
|
cv::Point3d vertex3d(static_cast<double>(vertex.x), |
|
|
|
|
static_cast<double>(vertex.y), |
|
|
|
|
static_cast<double>(vertex.z)); |
|
|
|
|
|
|
|
|
|
// 使用正确的ProjectPointP函数
|
|
|
|
|
// 从Camera头文件可以看到ProjectPointP返回TPoint2<TYPE>
|
|
|
|
|
cv::Point2d proj = imageData.camera.ProjectPointP<double>(vertex3d); |
|
|
|
|
|
|
|
|
|
// 检查是否在图像范围内(添加边界容差)
|
|
|
|
|
const float border = 5.0f; |
|
|
|
|
@ -11031,39 +11314,40 @@ bool MeshTexture::IsFaceVisibleFromView(FIndex idxFace, int viewID)
@@ -11031,39 +11314,40 @@ bool MeshTexture::IsFaceVisibleFromView(FIndex idxFace, int viewID)
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 检查深度(如果需要)
|
|
|
|
|
// 这里可以添加深度测试逻辑
|
|
|
|
|
// 可选:检查深度(点在相机前方)
|
|
|
|
|
// 可以使用camera.PointDepth函数
|
|
|
|
|
double depth = imageData.camera.PointDepth(vertex3d); |
|
|
|
|
if (depth <= 0) { |
|
|
|
|
return false; // 点在相机后面
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 检查面法线和视图方向的夹角
|
|
|
|
|
const Point3f& faceNormal = scene.mesh.faceNormals[idxFace]; |
|
|
|
|
const cv::Point3f& faceNormal = scene.mesh.faceNormals[idxFace]; |
|
|
|
|
|
|
|
|
|
// 计算面中心
|
|
|
|
|
const cv::Point3f faceCenter = (vertices[face[0]] + vertices[face[1]] + vertices[face[2]]) * (1.0f / 3.0f); |
|
|
|
|
cv::Point3f faceCenter = (vertices[face[0]] + vertices[face[1]] + vertices[face[2]]) * (1.0f / 3.0f); |
|
|
|
|
cv::Point3d faceCenter3d(faceCenter.x, faceCenter.y, faceCenter.z); |
|
|
|
|
|
|
|
|
|
// 计算相机中心
|
|
|
|
|
const cv::Point3f cameraCenter( |
|
|
|
|
static_cast<float>(imageData.camera.C.x), |
|
|
|
|
static_cast<float>(imageData.camera.C.y), |
|
|
|
|
static_cast<float>(imageData.camera.C.z) |
|
|
|
|
); |
|
|
|
|
const cv::Point3d& cameraCenter = imageData.camera.C; |
|
|
|
|
|
|
|
|
|
// 计算视图方向
|
|
|
|
|
cv::Point3f viewDir = cameraCenter - faceCenter; |
|
|
|
|
float viewLen = cv::norm(viewDir); |
|
|
|
|
cv::Point3d viewDir = cameraCenter - faceCenter3d; |
|
|
|
|
double viewLen = cv::norm(viewDir); |
|
|
|
|
|
|
|
|
|
if (viewLen > 0) { |
|
|
|
|
// 归一化
|
|
|
|
|
viewDir /= viewLen; |
|
|
|
|
|
|
|
|
|
// 计算法线点积
|
|
|
|
|
float cosAngle = faceNormal.x * viewDir.x + |
|
|
|
|
double cosAngle = faceNormal.x * viewDir.x + |
|
|
|
|
faceNormal.y * viewDir.y + |
|
|
|
|
faceNormal.z * viewDir.z; |
|
|
|
|
|
|
|
|
|
// 如果夹角太大(接近90度),面可能不可见
|
|
|
|
|
// 使用阈值cos(85°) ≈ 0.087
|
|
|
|
|
if (cosAngle < 0.1f) { |
|
|
|
|
if (cosAngle < 0.1) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -11071,7 +11355,6 @@ bool MeshTexture::IsFaceVisibleFromView(FIndex idxFace, int viewID)
@@ -11071,7 +11355,6 @@ bool MeshTexture::IsFaceVisibleFromView(FIndex idxFace, int viewID)
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 优化纹理接缝
|
|
|
|
|
void MeshTexture::OptimizeTextureSeams(const std::vector<cv::Mat>& textures, |
|
|
|
|
const std::vector<std::vector<Point2f>>& seamPoints) |
|
|
|
|
@ -11276,29 +11559,23 @@ void MeshTexture::LocalSeamLevelingEnhanced()
@@ -11276,29 +11559,23 @@ void MeshTexture::LocalSeamLevelingEnhanced()
|
|
|
|
|
if (viewID < 0 || viewID >= (int)images.size()) continue; |
|
|
|
|
|
|
|
|
|
const Image& image = images[viewID]; |
|
|
|
|
|
|
|
|
|
// 为面的每个顶点采样颜色
|
|
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
|
VIndex vid = face[i]; |
|
|
|
|
const cv::Point3f& vertex = vertices[vid]; |
|
|
|
|
|
|
|
|
|
// 方法1: 使用 OpenMVS 中的点类型
|
|
|
|
|
// 在 OpenMVS 中,通常使用 Point3f 而不是 cv::Point3f
|
|
|
|
|
// 我们需要转换类型
|
|
|
|
|
Point3f point(vertex.x, vertex.y, vertex.z); |
|
|
|
|
|
|
|
|
|
// 方法2: 使用 OpenMVS Camera 类的正确投影函数
|
|
|
|
|
// 根据 OpenMVS 源码,通常有两种方式:
|
|
|
|
|
// 1. camera.ProjectPoint(point) - 用于投影 3D 点
|
|
|
|
|
// 2. 或者通过相机矩阵手动计算
|
|
|
|
|
|
|
|
|
|
Point2f proj; |
|
|
|
|
// 将cv::Point3f转换为OpenMVS的Point3d类型
|
|
|
|
|
// Camera类使用double精度进行计算
|
|
|
|
|
Point3d point(static_cast<double>(vertex.x), |
|
|
|
|
static_cast<double>(vertex.y), |
|
|
|
|
static_cast<double>(vertex.z)); |
|
|
|
|
|
|
|
|
|
// 尝试第一种方法: 使用 ProjectPoint
|
|
|
|
|
proj = image.camera.ProjectPoint(point); |
|
|
|
|
// 使用ProjectPoint函数进行投影
|
|
|
|
|
Point2d projd = image.camera.ProjectPoint(point); |
|
|
|
|
|
|
|
|
|
// 如果上面失败,尝试第二种方法: 手动计算投影
|
|
|
|
|
// Point2f proj = ProjectPoint(point, image.camera.K, image.camera.R, image.camera.C);
|
|
|
|
|
// 转换为浮点数坐标用于图像采样
|
|
|
|
|
Point2f proj(static_cast<float>(projd.x), |
|
|
|
|
static_cast<float>(projd.y)); |
|
|
|
|
|
|
|
|
|
// 检查投影点是否在图像内
|
|
|
|
|
if (proj.x >= 0 && proj.x < image.image.cols && |
|
|
|
|
@ -11411,6 +11688,138 @@ void MeshTexture::LocalSeamLevelingEnhanced()
@@ -11411,6 +11688,138 @@ void MeshTexture::LocalSeamLevelingEnhanced()
|
|
|
|
|
// 要将这些颜色应用到纹理中,需要在纹理生成过程中使用这些颜色
|
|
|
|
|
// 这通常需要在纹理图集生成后,对纹理像素进行调整
|
|
|
|
|
} |
|
|
|
|
void MeshTexture::GlobalSeamLevelingEnhanced() { |
|
|
|
|
DEBUG_EXTRA("Starting enhanced global seam leveling..."); |
|
|
|
|
|
|
|
|
|
// 检查是否有纹理块
|
|
|
|
|
if (texturePatches.IsEmpty()) { |
|
|
|
|
DEBUG_EXTRA("No texture patches found. Skipping enhanced global seam leveling."); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 创建一个从面索引到纹理块索引的映射
|
|
|
|
|
std::vector<int> faceToPatchMap(faces.size(), -1); |
|
|
|
|
|
|
|
|
|
// 遍历所有纹理块,填充映射
|
|
|
|
|
for (int patchIdx = 0; patchIdx < (int)texturePatches.size(); ++patchIdx) { |
|
|
|
|
const TexturePatch& patch = texturePatches[patchIdx]; |
|
|
|
|
for (FIndex fid : patch.faces) { |
|
|
|
|
if (fid < faceToPatchMap.size()) { |
|
|
|
|
faceToPatchMap[fid] = patchIdx; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 创建顶点到纹理块的映射
|
|
|
|
|
std::vector<std::vector<uint32_t>> vertexLabels(vertices.size()); |
|
|
|
|
|
|
|
|
|
// 收集每个顶点的候选标签
|
|
|
|
|
for (FIndex idxFace = 0; idxFace < faces.size(); ++idxFace) { |
|
|
|
|
const Face& face = faces[idxFace]; |
|
|
|
|
int label = faceToPatchMap[idxFace]; |
|
|
|
|
|
|
|
|
|
if (label == -1) continue; // 没有分配纹理块的面
|
|
|
|
|
|
|
|
|
|
for (int v = 0; v < 3; ++v) { |
|
|
|
|
const VIndex idxVertex = face[v]; |
|
|
|
|
vertexLabels[idxVertex].push_back(label); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 移除重复标签并排序
|
|
|
|
|
for (auto& labels : vertexLabels) { |
|
|
|
|
std::sort(labels.begin(), labels.end()); |
|
|
|
|
labels.erase(std::unique(labels.begin(), labels.end()), labels.end()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 计算纹理块的平均颜色
|
|
|
|
|
std::vector<cv::Vec3f> patchColors(texturePatches.size(), cv::Vec3f(0, 0, 0)); |
|
|
|
|
std::vector<int> patchCounts(texturePatches.size(), 0); |
|
|
|
|
|
|
|
|
|
// 遍历所有面,计算纹理块颜色
|
|
|
|
|
for (FIndex idxFace = 0; idxFace < faces.size(); ++idxFace) { |
|
|
|
|
int label = faceToPatchMap[idxFace]; |
|
|
|
|
if (label == -1) continue; |
|
|
|
|
|
|
|
|
|
// 计算面的颜色(这里使用简单的颜色)
|
|
|
|
|
cv::Vec3f faceColor(0.5f, 0.5f, 0.5f); // 使用中性灰色作为默认颜色
|
|
|
|
|
|
|
|
|
|
patchColors[label] += faceColor; |
|
|
|
|
patchCounts[label]++; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < patchColors.size(); ++i) { |
|
|
|
|
if (patchCounts[i] > 0) { |
|
|
|
|
patchColors[i] /= (float)patchCounts[i]; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 使用简单的贪婪算法优化接缝
|
|
|
|
|
int changed = 0; |
|
|
|
|
for (FIndex idxFace = 0; idxFace < faces.size(); ++idxFace) { |
|
|
|
|
const Face& face = faces[idxFace]; |
|
|
|
|
int currentLabel = faceToPatchMap[idxFace]; |
|
|
|
|
|
|
|
|
|
if (currentLabel == -1) continue; |
|
|
|
|
|
|
|
|
|
// 收集相邻面的标签
|
|
|
|
|
std::unordered_map<int, int> neighborLabelCounts; |
|
|
|
|
|
|
|
|
|
for (int v = 0; v < 3; ++v) { |
|
|
|
|
const VIndex idxVertex = face[v]; |
|
|
|
|
|
|
|
|
|
// 获取与顶点相邻的面
|
|
|
|
|
// 在OpenMVS中,vertexFaces通常是一个包含索引数组的容器
|
|
|
|
|
if (idxVertex < scene.mesh.vertexFaces.size()) { |
|
|
|
|
// 使用auto自动推导类型,避免类型不匹配
|
|
|
|
|
const auto& adjacentFaces = scene.mesh.vertexFaces[idxVertex]; |
|
|
|
|
|
|
|
|
|
for (FIndex adjFaceIdx : adjacentFaces) { |
|
|
|
|
if (adjFaceIdx == idxFace) continue; |
|
|
|
|
|
|
|
|
|
int neighborLabel = faceToPatchMap[adjFaceIdx]; |
|
|
|
|
if (neighborLabel != -1) { |
|
|
|
|
neighborLabelCounts[neighborLabel]++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 如果没有相邻面,跳过
|
|
|
|
|
if (neighborLabelCounts.empty()) continue; |
|
|
|
|
|
|
|
|
|
// 找到出现次数最多的相邻标签
|
|
|
|
|
int bestLabel = currentLabel; |
|
|
|
|
int maxCount = 0; |
|
|
|
|
|
|
|
|
|
for (const auto& pair : neighborLabelCounts) { |
|
|
|
|
if (pair.second > maxCount) { |
|
|
|
|
maxCount = pair.second; |
|
|
|
|
bestLabel = pair.first; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 如果最佳标签与当前不同,且满足一定条件,则更改标签
|
|
|
|
|
if (bestLabel != currentLabel && maxCount >= 2) { |
|
|
|
|
// 计算颜色差异
|
|
|
|
|
float colorDiff = cv::norm(patchColors[currentLabel], patchColors[bestLabel]); |
|
|
|
|
|
|
|
|
|
// 如果颜色差异不大,则更改标签
|
|
|
|
|
if (colorDiff < 0.3f) { // 阈值可根据需要调整
|
|
|
|
|
// 更新面的纹理块映射
|
|
|
|
|
// 注意:这需要更新texturePatches中的面分配
|
|
|
|
|
// 这里我们只是更新映射,实际的纹理块更新需要更多操作
|
|
|
|
|
faceToPatchMap[idxFace] = bestLabel; |
|
|
|
|
changed++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Enhanced global seam leveling completed. Changed %d face labels.", changed); |
|
|
|
|
|
|
|
|
|
// 注意:这里只是更新了faceToPatchMap,实际更新纹理块需要更多操作
|
|
|
|
|
// 可能需要重新构建纹理块或更新纹理坐标
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 合并重叠的纹理块
|
|
|
|
|
void MeshTexture::MergeOverlappingPatches(Mesh::TexCoordArr& faceTexcoords2) |
|
|
|
|
@ -11450,8 +11859,8 @@ void MeshTexture::MergeOverlappingPatches(Mesh::TexCoordArr& faceTexcoords2)
@@ -11450,8 +11859,8 @@ void MeshTexture::MergeOverlappingPatches(Mesh::TexCoordArr& faceTexcoords2)
|
|
|
|
|
// 计算两个纹理块的重叠区域
|
|
|
|
|
cv::Rect intersection = patch1.rect & patch2.rect; |
|
|
|
|
if (!intersection.empty()) { |
|
|
|
|
float overlapArea = intersection.area(); |
|
|
|
|
float minArea = std::min(patch1.rect.area(), patch2.rect.area()); |
|
|
|
|
float overlapArea = (float)intersection.area(); |
|
|
|
|
float minArea = std::min((float)patch1.rect.area(), (float)patch2.rect.area()); |
|
|
|
|
float overlapRatio = overlapArea / minArea; |
|
|
|
|
|
|
|
|
|
if (overlapRatio > 0.1f) { // 重叠超过10%
|
|
|
|
|
@ -11484,31 +11893,40 @@ void MeshTexture::MergeOverlappingPatches(Mesh::TexCoordArr& faceTexcoords2)
@@ -11484,31 +11893,40 @@ void MeshTexture::MergeOverlappingPatches(Mesh::TexCoordArr& faceTexcoords2)
|
|
|
|
|
// 计算合并后的边界框
|
|
|
|
|
cv::Rect mergedRect = patch1.rect | patch2.rect; |
|
|
|
|
|
|
|
|
|
// 合并面列表
|
|
|
|
|
FaceIdxArr mergedFaces = patch1.faces; |
|
|
|
|
for (FIndex fid : patch2.faces) { |
|
|
|
|
mergedFaces.Insert(fid); |
|
|
|
|
// 合并面列表 - 使用 std::vector 代替 FIndexArr
|
|
|
|
|
std::vector<FIndex> mergedFaces; |
|
|
|
|
// 从 patch1.faces 复制
|
|
|
|
|
for (size_t k = 0; k < patch1.faces.GetSize(); ++k) { |
|
|
|
|
mergedFaces.push_back(patch1.faces[k]); |
|
|
|
|
} |
|
|
|
|
// 从 patch2.faces 添加
|
|
|
|
|
for (size_t k = 0; k < patch2.faces.GetSize(); ++k) { |
|
|
|
|
mergedFaces.push_back(patch2.faces[k]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 创建新的纹理块
|
|
|
|
|
patch1.rect = mergedRect; |
|
|
|
|
patch1.faces = mergedFaces; |
|
|
|
|
// 将 std::vector 转回 patch1.faces
|
|
|
|
|
patch1.faces.Release(); |
|
|
|
|
for (FIndex fid : mergedFaces) { |
|
|
|
|
patch1.faces.Insert(fid); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 标记第二个纹理块为已合并
|
|
|
|
|
patch2.label = NO_ID; |
|
|
|
|
patch2.faces.Release(); |
|
|
|
|
merged[idx2] = true; |
|
|
|
|
|
|
|
|
|
DEBUG_VERBOSE("Merged patches %d and %d (overlap: %.2f%%)", |
|
|
|
|
DEBUG_EXTRA("Merged patches %d and %d (overlap: %.2f%%)", |
|
|
|
|
idx1, idx2, overlap.first * 100.0f); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 移除空的纹理块
|
|
|
|
|
TexturePatchIdxArr newIndices(texturePatches.size()); |
|
|
|
|
std::vector<int> newIndices(texturePatches.size(), NO_ID); |
|
|
|
|
int newIdx = 0; |
|
|
|
|
for (int i = 0; i < (int)texturePatches.size(); ++i) { |
|
|
|
|
if (texturePatches[i].label != NO_ID && !texturePatches[i].faces.IsEmpty()) { |
|
|
|
|
if (texturePatches[i].label != NO_ID && texturePatches[i].faces.GetSize() > 0) { |
|
|
|
|
texturePatches[newIdx] = texturePatches[i]; |
|
|
|
|
newIndices[i] = newIdx; |
|
|
|
|
++newIdx; |
|
|
|
|
@ -11518,14 +11936,6 @@ void MeshTexture::MergeOverlappingPatches(Mesh::TexCoordArr& faceTexcoords2)
@@ -11518,14 +11936,6 @@ void MeshTexture::MergeOverlappingPatches(Mesh::TexCoordArr& faceTexcoords2)
|
|
|
|
|
} |
|
|
|
|
texturePatches.Resize(newIdx); |
|
|
|
|
|
|
|
|
|
// 更新面的纹理索引
|
|
|
|
|
for (FIndex fid = 0; fid < (FIndex)faces.size(); ++fid) { |
|
|
|
|
int oldIdx = faceTexidx[fid]; |
|
|
|
|
if (oldIdx != NO_ID) { |
|
|
|
|
faceTexidx[fid] = newIndices[oldIdx]; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Merged overlapping patches, new count: %d", texturePatches.size()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -11742,7 +12152,7 @@ void MeshTexture::FillTextureHoles(std::vector<Image8U3>& textures, Pixel8U colE
@@ -11742,7 +12152,7 @@ void MeshTexture::FillTextureHoles(std::vector<Image8U3>& textures, Pixel8U colE
|
|
|
|
|
continue; // 没有空洞
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_VERBOSE("Texture %zu: filling %d holes", i, holeCount); |
|
|
|
|
// DEBUG_VERBOSE("Texture %zu: filling %d holes", i, holeCount);
|
|
|
|
|
|
|
|
|
|
// 使用修复算法填充空洞
|
|
|
|
|
cv::Mat inpainted; |
|
|
|
|
|