Browse Source

中间版本

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

172
libs/MVS/SceneTexture.cpp

@ -10860,96 +10860,140 @@ Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasFromExistingTextures(const La @@ -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 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 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边界框
AABB2f faceUVBounds(true);
for (int i = 0; i < 3; ++i) {
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));
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));
// 检查边界框是否有效
if (startX >= endX || startY >= endY) {
continue;
// 计算三角形在纹理空间中的边界框
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);
// 转换到像素坐标,并扩展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 (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,39 +11011,33 @@ Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasFromImages(const LabelArr& fa @@ -10967,39 +11011,33 @@ 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);
// 更新统计
if (facePixelsFilled > 0) {
totalPixelsFilled += facePixelsFilled;
processedFaces++;
}
// 检查生成的纹理
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 ((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);
ValidateGeneratedTexture(textureAtlas, "生成的纹理");
// 计算填充率
int totalPixels = textureSize * textureSize;
float fillRate = (totalPixelsFilled * 100.0f) / totalPixels;
DEBUG_EXTRA("纹理填充率: %.2f%% (%d/%d)", fillRate, totalPixelsFilled, totalPixels);
return textures;
}

Loading…
Cancel
Save