diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 78d27c4..a4992b6 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -10860,96 +10860,140 @@ Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasFromExistingTextures(const La Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasFromImages(const LabelArr& faceLabels, const IIndexArr& views, int textureSize, Pixel8U colEmpty, float fSharpnessWeight) { + DEBUG_EXTRA("===== 开始从图像生成纹理图集 ====="); + DEBUG_EXTRA("纹理尺寸: %dx%d", textureSize, textureSize); + Mesh::Image8U3Arr textures; + + // 创建纹理 textures.push_back(Image8U3()); Image8U3& textureAtlas = textures.back(); textureAtlas.create(textureSize, textureSize); + + if (textureAtlas.empty()) { + DEBUG_EXTRA("错误: 无法创建纹理!"); + return textures; + } + + // 设置背景色 textureAtlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); - // 计算整个模型的UV范围 + // 计算UV坐标范围 AABB2f globalUVBounds(true); FOREACH(i, scene.mesh.faceTexcoords) { const TexCoord& uv = scene.mesh.faceTexcoords[i]; globalUVBounds.InsertFull(uv); } + DEBUG_EXTRA("全局UV范围: 最小=(%.3f,%.3f), 最大=(%.3f,%.3f)", + globalUVBounds.ptMin.x(), globalUVBounds.ptMin.y(), + globalUVBounds.ptMax.x(), globalUVBounds.ptMax.y()); + float uvWidth = globalUVBounds.ptMax.x() - globalUVBounds.ptMin.x(); float uvHeight = globalUVBounds.ptMax.y() - globalUVBounds.ptMin.y(); - DEBUG_EXTRA("全局UV范围: 最小=(%.3f,%.3f), 最大=(%.3f,%.3f), 宽度=%.3f, 高度=%.3f", - globalUVBounds.ptMin.x(), globalUVBounds.ptMin.y(), - globalUVBounds.ptMax.x(), globalUVBounds.ptMax.y(), - uvWidth, uvHeight); + DEBUG_EXTRA("UV尺寸: 宽度=%.3f, 高度=%.3f", uvWidth, uvHeight); + + // 检查是否需要归一化 + bool needNormalize = (globalUVBounds.ptMin.x() < 0.0f || globalUVBounds.ptMin.y() < 0.0f || + globalUVBounds.ptMax.x() > 1.0f || globalUVBounds.ptMax.y() > 1.0f); - bool normalizeUV = (uvWidth > 100.0f || uvHeight > 100.0f); - DEBUG_EXTRA("是否需要归一化UV坐标: %s", normalizeUV ? "是" : "否"); + if (needNormalize) { + DEBUG_EXTRA("警告: UV坐标不在[0,1]范围内,需要进行归一化处理"); + } - // 为每个面片从原始图像采样颜色 + int totalFaces = (int)scene.mesh.faces.size(); int processedFaces = 0; - int sampledPixels = 0; + int totalPixelsFilled = 0; + + DEBUG_EXTRA("开始处理 %d 个面片", totalFaces); + // 为每个面片生成纹理 #ifdef _USE_OPENMP - #pragma omp parallel for schedule(dynamic) reduction(+:processedFaces,sampledPixels) - for (int_t idxFace = 0; idxFace < (int_t)scene.mesh.faces.size(); ++idxFace) { + #pragma omp parallel for schedule(dynamic) reduction(+:processedFaces,totalPixelsFilled) + for (int idxFace = 0; idxFace < totalFaces; ++idxFace) { #else FOREACH(idxFace, scene.mesh.faces) { #endif const FIndex faceID = (FIndex)idxFace; const Label label = faceLabels[faceID]; - if (label == 0) continue; // 跳过无视图的面片 + + if (label == 0) { + // 跳过无视图的面片 + continue; + } const IIndex idxView = label - 1; - if (idxView >= images.size()) continue; + if (idxView >= images.size()) { + continue; + } // 获取面的UV坐标 - if (faceID * 3 + 2 >= scene.mesh.faceTexcoords.size()) continue; + if (faceID * 3 + 2 >= scene.mesh.faceTexcoords.size()) { + DEBUG_EXTRA("警告: 面片 %d 缺少UV坐标", faceID); + continue; + } const TexCoord* uvCoords = &scene.mesh.faceTexcoords[faceID * 3]; const Face& face = scene.mesh.faces[faceID]; const Image& sourceImage = images[idxView]; - // 将UV坐标归一化到[0,1]范围 - TexCoord normalizedUVs[3]; + // 将UV坐标转换到纹理空间 + TexCoord texCoords[3]; for (int i = 0; i < 3; ++i) { - if (normalizeUV) { - // 归一化到[0,1]范围 - normalizedUVs[i].x = (uvCoords[i].x - globalUVBounds.ptMin.x()) / uvWidth; - normalizedUVs[i].y = (uvCoords[i].y - globalUVBounds.ptMin.y()) / uvHeight; - } else { - // 如果UV已经是[0,1]范围内,直接使用 - normalizedUVs[i] = uvCoords[i]; + // 计算归一化的UV坐标 + float u = uvCoords[i].x; + float v = uvCoords[i].y; + + // 如果UV坐标不在[0,1]范围内,进行归一化 + if (needNormalize) { + u = (u - globalUVBounds.ptMin.x()) / uvWidth; + v = (v - globalUVBounds.ptMin.y()) / uvHeight; + + // 确保在[0,1]范围内 + u = std::max(0.0f, std::min(1.0f, u)); + v = std::max(0.0f, std::min(1.0f, v)); } - // 确保在[0,1]范围内 - normalizedUVs[i].x = std::max(0.0f, std::min(1.0f, normalizedUVs[i].x)); - normalizedUVs[i].y = std::max(0.0f, std::min(1.0f, normalizedUVs[i].y)); + // 转换到像素坐标 + texCoords[i].x = u * (textureSize - 1); + texCoords[i].y = v * (textureSize - 1); + + // 调试:输出前几个面片的UV坐标 + if (processedFaces < 5 && i == 0) { + DEBUG_EXTRA("面片 %d: 原始UV=(%.6f,%.6f), 归一化UV=(%.6f,%.6f), 像素坐标=(%.1f,%.1f)", + faceID, uvCoords[i].x, uvCoords[i].y, u, v, + texCoords[i].x, texCoords[i].y); + } } - // 计算归一化后的UV边界框 - AABB2f faceUVBounds(true); - for (int i = 0; i < 3; ++i) { - faceUVBounds.InsertFull(normalizedUVs[i]); - } + // 计算三角形在纹理空间中的边界框 + float minX = std::min(std::min(texCoords[0].x, texCoords[1].x), texCoords[2].x); + float minY = std::min(std::min(texCoords[0].y, texCoords[1].y), texCoords[2].y); + float maxX = std::max(std::max(texCoords[0].x, texCoords[1].x), texCoords[2].x); + float maxY = std::max(std::max(texCoords[0].y, texCoords[1].y), texCoords[2].y); - // 将UV坐标转换到纹理像素坐标 - const int startX = std::max(0, (int)(faceUVBounds.ptMin.x() * textureSize)); - const int startY = std::max(0, (int)(faceUVBounds.ptMin.y() * textureSize)); - const int endX = std::min(textureSize - 1, (int)(faceUVBounds.ptMax.x() * textureSize)); - const int endY = std::min(textureSize - 1, (int)(faceUVBounds.ptMax.y() * textureSize)); + // 转换到像素坐标,并扩展1个像素确保覆盖边缘 + int startX = std::max(0, (int)std::floor(minX) - 1); + int startY = std::max(0, (int)std::floor(minY) - 1); + int endX = std::min(textureSize - 1, (int)std::ceil(maxX) + 1); + int endY = std::min(textureSize - 1, (int)std::ceil(maxY) + 1); - // 检查边界框是否有效 - if (startX >= endX || startY >= endY) { - continue; + // 调试:输出前几个面片的边界框 + if (processedFaces < 5) { + DEBUG_EXTRA("面片 %d 纹理边界框: [%d,%d]-[%d,%d]", faceID, startX, startY, endX, endY); } - // 对每个像素进行采样 + int facePixelsFilled = 0; + + // 对边界框内的每个像素进行处理 for (int y = startY; y <= endY; ++y) { for (int x = startX; x <= endX; ++x) { - const Point2f texCoord((float)x / textureSize, (float)y / textureSize); + const Point2f texPoint(x, y); - // 检查是否在三角形内 + // 计算重心坐标 Point3f barycentric; - if (PointInTriangle(texCoord, normalizedUVs[0], normalizedUVs[1], normalizedUVs[2], barycentric)) { + if (PointInTriangle(texPoint, texCoords[0], texCoords[1], texCoords[2], barycentric)) { // 计算3D空间中的对应点 const Vertex worldPoint = vertices[face[0]] * barycentric.x + @@ -10967,40 +11011,34 @@ Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasFromImages(const LabelArr& fa Pixel8U sampledColor = SampleImageBilinear(sourceImage.image, imgPoint); // 写入纹理图集 - #ifdef _USE_OPENMP - #pragma omp critical - #endif - { - textureAtlas(y, x) = sampledColor; - sampledPixels++; - } + textureAtlas(y, x) = sampledColor; + facePixelsFilled++; } } } } - processedFaces++; - } - - DEBUG_EXTRA("纹理生成统计: 处理了%d个面片, 采样了%d个像素", processedFaces, sampledPixels); - - // 检查生成的纹理 - int coloredPixels = 0; - for (int y = 0; y < textureAtlas.rows; ++y) { - for (int x = 0; x < textureAtlas.cols; ++x) { - const Pixel8U& pixel = textureAtlas(y, x); - if (!(pixel[0] == colEmpty.b && pixel[1] == colEmpty.g && pixel[2] == colEmpty.r)) { - coloredPixels++; - } + // 更新统计 + if (facePixelsFilled > 0) { + totalPixelsFilled += facePixelsFilled; + processedFaces++; + } + + // 显示进度 + if ((processedFaces % 1000) == 0) { + DEBUG_EXTRA("处理进度: %d/%d 个面片, 填充了 %d 个像素", + processedFaces, totalFaces, totalPixelsFilled); } } - DEBUG_EXTRA("纹理填充率: %d/%d (%.2f%%)", - coloredPixels, textureAtlas.rows * textureAtlas.cols, - (coloredPixels * 100.0) / (textureAtlas.rows * textureAtlas.cols)); + DEBUG_EXTRA("纹理生成完成: 处理了 %d/%d 个面片, 总共填充了 %d 个像素", + processedFaces, totalFaces, totalPixelsFilled); + + // 计算填充率 + int totalPixels = textureSize * textureSize; + float fillRate = (totalPixelsFilled * 100.0f) / totalPixels; + DEBUG_EXTRA("纹理填充率: %.2f%% (%d/%d)", fillRate, totalPixelsFilled, totalPixels); - ValidateGeneratedTexture(textureAtlas, "生成的纹理"); - return textures; }