Browse Source

中间版本

ManualUV
hesuicong 4 weeks ago
parent
commit
80c0dcbb49
  1. 162
      libs/MVS/SceneTexture.cpp

162
libs/MVS/SceneTexture.cpp

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

Loading…
Cancel
Save