Browse Source

回退到上个版本

ManualUV
hesuicong 4 weeks ago
parent
commit
f787e874aa
  1. 368
      libs/MVS/SceneTexture.cpp

368
libs/MVS/SceneTexture.cpp

@ -492,6 +492,16 @@ public:
const Mesh::TexCoordArr& existingTexcoords, // 添加已有UV参数 const Mesh::TexCoordArr& existingTexcoords, // 添加已有UV参数
const Mesh::TexIndexArr& existingTexindices // 添加已有纹理索引参数 const Mesh::TexIndexArr& existingTexindices // 添加已有纹理索引参数
); );
void CheckColorChannels(const Image8U3& texture, const std::string& name);
Mesh::Image8U3Arr GenerateTextureAtlasWith3DBridge(
const LabelArr& faceLabels,
const IIndexArr& views,
const Mesh::Image8U3Arr& sourceTextures,
const Mesh::TexCoordArr& sourceTexcoords,
const Mesh::TexIndexArr& sourceTexindices,
unsigned nTextureSizeMultiple,
Pixel8U colEmpty,
float fSharpnessWeight);
Mesh::Image8U3Arr GenerateTextureAtlasFromUV( Mesh::Image8U3Arr GenerateTextureAtlasFromUV(
const Mesh::Image8U3Arr& sourceTextures, // 已有纹理数组 const Mesh::Image8U3Arr& sourceTextures, // 已有纹理数组
const Mesh::TexCoordArr& sourceTexcoords, // 已有UV坐标 const Mesh::TexCoordArr& sourceTexcoords, // 已有UV坐标
@ -9945,6 +9955,26 @@ bool SaveGeneratedTextures(const Mesh::Image8U3Arr& generatedTextures, const std
return true; return true;
} }
void MeshTexture::CheckColorChannels(const Image8U3& texture, const std::string& name) {
if (texture.empty()) {
DEBUG_EXTRA("%s: 纹理为空", name.c_str());
return;
}
// 检查左上角几个像素的颜色
for (int y = 0; y < std::min(3, texture.rows); ++y) {
for (int x = 0; x < std::min(3, texture.cols); ++x) {
const cv::Vec3b& pixel = texture.at<cv::Vec3b>(y, x);
DEBUG_EXTRA("%s[%d,%d]: B=%d, G=%d, R=%d",
name.c_str(), x, y, pixel[0], pixel[1], pixel[2]);
}
}
// 计算平均颜色
cv::Scalar mean = cv::mean(texture);
DEBUG_EXTRA("%s 平均颜色: B=%.1f, G=%.1f, R=%.1f",
name.c_str(), mean[0], mean[1], mean[2]);
}
bool MeshTexture::TextureWithExistingUV( bool MeshTexture::TextureWithExistingUV(
const IIndexArr& views, const IIndexArr& views,
@ -9953,12 +9983,11 @@ bool MeshTexture::TextureWithExistingUV(
unsigned nTextureSizeMultiple, unsigned nTextureSizeMultiple,
Pixel8U colEmpty, Pixel8U colEmpty,
float fSharpnessWeight, float fSharpnessWeight,
const Mesh::Image8U3Arr& existingTextures, // 添加已有纹理参数 const Mesh::Image8U3Arr& existingTextures,
const Mesh::TexCoordArr& existingTexcoords, // 添加已有UV参数 const Mesh::TexCoordArr& existingTexcoords,
const Mesh::TexIndexArr& existingTexindices // 添加已有纹理索引参数 const Mesh::TexIndexArr& existingTexindices)
)
{ {
DEBUG_EXTRA("TextureWithExistingUV - Using existing texture data"); DEBUG_EXTRA("TextureWithExistingUV - 使用3D几何坐标作为桥梁");
TD_TIMER_START(); TD_TIMER_START();
// 1. 验证输入 // 1. 验证输入
@ -9967,38 +9996,64 @@ bool MeshTexture::TextureWithExistingUV(
return false; return false;
} }
if (existingTextures.empty() || existingTexcoords.empty()) { if (existingTextures.empty()) {
VERBOSE("error: no existing texture data provided"); VERBOSE("error: no existing texture data provided");
return false; return false;
} }
// 验证UV坐标数量匹配 DEBUG_EXTRA("Processing %zu faces with existing texture data", scene.mesh.faces.size());
if (existingTexcoords.size() != scene.mesh.faceTexcoords.size()) {
VERBOSE("error: UV coordinate count mismatch: existing=%zu, mesh=%zu", // 2. 为每个面选择最佳视图(从原始图像,而不是已有纹理)
existingTexcoords.size(), scene.mesh.faceTexcoords.size()); FaceDataViewArr facesDatas;
if (!ListCameraFaces(facesDatas, fOutlierThreshold, nIgnoreMaskLabel, views, false)) {
return false; return false;
} }
DEBUG_EXTRA("Processing %zu faces with existing texture data", scene.mesh.faces.size()); // 3. 为每个面分配最佳视图
LabelArr faceLabels(scene.mesh.faces.size());
FOREACH(idxFace, scene.mesh.faces) {
const FaceDataArr& faceDatas = facesDatas[idxFace];
if (faceDatas.empty()) {
faceLabels[idxFace] = 0; // 无视图可用
continue;
}
// 选择质量最高的视图
float bestQuality = -1;
IIndex bestView = NO_ID;
for (const FaceData& data : faceDatas) {
if (data.quality > bestQuality && !data.bInvalidFacesRelative) {
bestQuality = data.quality;
bestView = data.idxView;
}
}
// 2. 生成纹理图集(从已有纹理采样,但使用模型原始UV) faceLabels[idxFace] = (bestView != NO_ID) ? (bestView + 1) : 0;
Mesh::Image8U3Arr generatedTextures = GenerateTextureAtlasFromUV( }
existingTextures,
existingTexcoords, // 4. 生成纹理图集
existingTexindices, Mesh::Image8U3Arr generatedTextures = GenerateTextureAtlasWith3DBridge(
nTextureSizeMultiple, faceLabels, views, existingTextures, existingTexcoords, existingTexindices,
colEmpty, nTextureSizeMultiple, colEmpty, fSharpnessWeight
fSharpnessWeight
); );
if (!generatedTextures.empty()) { if (!generatedTextures.empty()) {
// 检查颜色通道
CheckColorChannels(generatedTextures[0], "生成的纹理");
// 检查源纹理颜色通道
if (!existingTextures.empty()) {
CheckColorChannels(existingTextures[0], "源纹理");
}
// 保存纹理
scene.mesh.texturesDiffuse = std::move(generatedTextures); scene.mesh.texturesDiffuse = std::move(generatedTextures);
// 设置面的纹理索引 // 设置面的纹理索引
if (scene.mesh.texturesDiffuse.size() > 1) { if (scene.mesh.texturesDiffuse.size() > 1) {
scene.mesh.faceTexindices.resize(scene.mesh.faces.size()); scene.mesh.faceTexindices.resize(scene.mesh.faces.size());
for (size_t i = 0; i < scene.mesh.faces.size(); ++i) { for (size_t i = 0; i < scene.mesh.faces.size(); ++i) {
scene.mesh.faceTexindices[i] = 0; // 所有面使用第一个纹理 scene.mesh.faceTexindices[i] = 0;
} }
} else { } else {
scene.mesh.faceTexindices.Release(); scene.mesh.faceTexindices.Release();
@ -10006,7 +10061,6 @@ bool MeshTexture::TextureWithExistingUV(
DEBUG_EXTRA("Generated %zu textures from existing data", DEBUG_EXTRA("Generated %zu textures from existing data",
scene.mesh.texturesDiffuse.size()); scene.mesh.texturesDiffuse.size());
return true; return true;
} }
@ -10014,6 +10068,221 @@ bool MeshTexture::TextureWithExistingUV(
return false; return false;
} }
Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasWith3DBridge(
const LabelArr& faceLabels,
const IIndexArr& views,
const Mesh::Image8U3Arr& sourceTextures,
const Mesh::TexCoordArr& sourceTexcoords,
const Mesh::TexIndexArr& sourceTexindices,
unsigned nTextureSizeMultiple,
Pixel8U colEmpty,
float fSharpnessWeight)
{
DEBUG_EXTRA("GenerateTextureAtlasWith3DBridge - 使用3D几何坐标作为桥梁");
// 1. 分析外部UV布局
AABB2f uvBounds(true);
FOREACH(i, scene.mesh.faceTexcoords) {
const TexCoord& uv = scene.mesh.faceTexcoords[i];
uvBounds.InsertFull(uv);
}
// 确保UV在[0,1]范围内
if (uvBounds.ptMin.x() < 0 || uvBounds.ptMin.y() < 0 ||
uvBounds.ptMax.x() > 1 || uvBounds.ptMax.y() > 1) {
DEBUG_EXTRA("UV超出[0,1]范围,进行归一化");
uvBounds = AABB2f(true);
for (size_t i = 0; i < scene.mesh.faceTexcoords.size(); i += 3) {
for (int v = 0; v < 3; ++v) {
const TexCoord& uv = scene.mesh.faceTexcoords[i + v];
uvBounds.InsertFull(uv);
}
}
}
// 计算纹理尺寸
const float uvWidth = uvBounds.ptMax.x() - uvBounds.ptMin.x();
const float uvHeight = uvBounds.ptMax.y() - uvBounds.ptMin.y();
const int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple);
// 创建目标纹理
Mesh::Image8U3Arr textures;
Image8U3& textureAtlas = textures.emplace_back(textureSize, textureSize);
// 注意:cv::Scalar 使用 BGR 顺序
textureAtlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r));
DEBUG_EXTRA("生成纹理图集: 尺寸=%dx%d, UV范围=[%.3f,%.3f]-[%.3f,%.3f]",
textureSize, textureSize,
uvBounds.ptMin.x(), uvBounds.ptMin.y(),
uvBounds.ptMax.x(), uvBounds.ptMax.y());
// 添加调试信息
DEBUG_EXTRA("colEmpty: R=%d, G=%d, B=%d", colEmpty.r, colEmpty.g, colEmpty.b);
DEBUG_EXTRA("OpenCV图像通道顺序: BGR");
// 检查颜色通道顺序
if (!sourceTextures.empty()) {
const Image8U3& firstTex = sourceTextures[0];
cv::Vec3b firstPixel = firstTex.at<cv::Vec3b>(0, 0);
DEBUG_EXTRA("源纹理第一个像素: B=%d, G=%d, R=%d",
firstPixel[0], firstPixel[1], firstPixel[2]);
}
// 3. 统计信息
int processedFaces = 0;
int sampledPixels = 0;
int failedFaces = 0;
// 4. 为每个面采样
#ifdef _USE_OPENMP
#pragma omp parallel for schedule(dynamic) reduction(+:processedFaces, sampledPixels, failedFaces)
#endif
for (int_t idxFace = 0; idxFace < (int_t)scene.mesh.faces.size(); ++idxFace) {
const FIndex faceID = (FIndex)idxFace;
const Label label = faceLabels[faceID];
if (label == 0) {
failedFaces++;
continue;
}
const IIndex idxView = label - 1;
if (idxView >= images.size()) {
failedFaces++;
continue;
}
// 获取面的几何信息
const TexCoord* meshUVs = &scene.mesh.faceTexcoords[faceID * 3];
const Face& face = scene.mesh.faces[faceID];
// 获取源纹理信息
const TexCoord* srcUVs = &sourceTexcoords[faceID * 3];
const TexIndex textureIdx = sourceTexindices.empty() ? 0 : sourceTexindices[faceID];
if (textureIdx >= sourceTextures.size()) {
failedFaces++;
continue;
}
const Image8U3& sourceTexture = sourceTextures[textureIdx];
// 计算面的UV边界
AABB2f faceBounds(true);
for (int i = 0; i < 3; ++i) {
faceBounds.InsertFull(meshUVs[i]);
}
// 转换为像素坐标
int startX = (int)(faceBounds.ptMin.x() * textureSize);
int startY = (int)(faceBounds.ptMin.y() * textureSize);
int endX = (int)(faceBounds.ptMax.x() * textureSize);
int endY = (int)(faceBounds.ptMax.y() * textureSize);
// 边界检查
startX = std::max(0, std::min(startX, textureSize - 1));
startY = std::max(0, std::min(startY, textureSize - 1));
endX = std::max(0, std::min(endX, textureSize - 1));
endY = std::max(0, std::min(endY, textureSize - 1));
if (startX >= endX || startY >= endY) {
failedFaces++;
continue;
}
int faceSampledPixels = 0;
// 采样纹理
for (int y = startY; y <= endY; ++y) {
for (int x = startX; x <= endX; ++x) {
const Point2f texCoord((x + 0.5f) / textureSize, (y + 0.5f) / textureSize);
// 计算重心坐标
Point3f barycentric;
if (!PointInTriangle(texCoord, meshUVs[0], meshUVs[1], meshUVs[2], barycentric)) {
continue;
}
// 计算3D点
const Vertex worldPoint =
vertices[face[0]] * barycentric.x +
vertices[face[1]] * barycentric.y +
vertices[face[2]] * barycentric.z;
// 方案A:从原始图像采样
Point2f imgPoint = ProjectPointWithAutoCorrection(images[idxView].camera, worldPoint, images[idxView]);
if (imgPoint.x < 0 || imgPoint.x >= images[idxView].image.cols ||
imgPoint.y < 0 || imgPoint.y >= images[idxView].image.rows) {
continue;
}
// 修正:使用双线性插值采样
const int x0 = (int)floor(imgPoint.x);
const int y0 = (int)floor(imgPoint.y);
const int x1 = std::min(x0 + 1, images[idxView].image.cols - 1);
const int y1 = std::min(y0 + 1, images[idxView].image.rows - 1);
const float fx = imgPoint.x - x0;
const float fy = imgPoint.y - y0;
const float fx1 = 1.0f - fx;
const float fy1 = 1.0f - fy;
// 采样四个点的颜色
const cv::Vec3b& c00 = images[idxView].image.at<cv::Vec3b>(y0, x0);
const cv::Vec3b& c01 = images[idxView].image.at<cv::Vec3b>(y0, x1);
const cv::Vec3b& c10 = images[idxView].image.at<cv::Vec3b>(y1, x0);
const cv::Vec3b& c11 = images[idxView].image.at<cv::Vec3b>(y1, x1);
// 双线性插值
const cv::Vec3b color =
c00 * (fx1 * fy1) +
c01 * (fx * fy1) +
c10 * (fx1 * fy) +
c11 * (fx * fy);
// 注意:OpenCV是BGR顺序,而Pixel8U通常是RGB顺序
// 这里我们需要确保颜色通道正确
#ifdef _USE_OPENMP
#pragma omp critical
#endif
{
// 方案1:直接使用BGR顺序(OpenCV默认)
// textureAtlas.at<cv::Vec3b>(y, x) = color;
// 方案2:交换B和R通道(如果颜色偏蓝,说明B和R反了)
// 将BGR转换为RGB
textureAtlas.at<cv::Vec3b>(y, x) = cv::Vec3b(color[2], color[1], color[0]);
}
faceSampledPixels++;
}
}
if (faceSampledPixels > 0) {
processedFaces++;
sampledPixels += faceSampledPixels;
} else {
failedFaces++;
}
}
DEBUG_EXTRA("纹理采样完成: 成功 %d 个面, 失败 %d 个面, 采样 %d 像素",
processedFaces, failedFaces, sampledPixels);
// 5. 填充空洞
if (processedFaces > 0 && sampledPixels > 0) {
// FillTextureGaps(textureAtlas, scene.mesh.faceTexcoords, (FIndex)scene.mesh.faces.size(), textureSize, colEmpty);
}
// 6. 锐化处理
if (fSharpnessWeight > 0) {
// ApplySharpening(textureAtlas, fSharpnessWeight);
}
return textures;
}
Point2f MeshTexture::ProjectPointWithAutoCorrection(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage) { Point2f MeshTexture::ProjectPointWithAutoCorrection(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage) {
Point2f imgPoint = camera.ProjectPointP(worldPoint); Point2f imgPoint = camera.ProjectPointP(worldPoint);
@ -11171,41 +11440,52 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
DEBUG_EXTRA("TextureMesh %b, %s", bUseExistingUV, strUVMeshFileName.c_str()); DEBUG_EXTRA("TextureMesh %b, %s", bUseExistingUV, strUVMeshFileName.c_str());
if (bUseExistingUV && !strUVMeshFileName.empty()) { if (bUseExistingUV && !strUVMeshFileName.empty()) {
// 1. 生成临时纹理和UV
Mesh::TexCoordArr existingTexcoords; Mesh::TexCoordArr existingTexcoords;
Mesh::TexIndexArr existingTexindices; Mesh::TexIndexArr existingTexindices;
texture.GenerateTextureForUV(bGlobalSeamLeveling, bLocalSeamLeveling, nTextureSizeMultiple, nRectPackingHeuristic, colEmpty, fSharpnessWeight, maxTextureSize, baseFileName, bOriginFaceview, this, existingTexcoords, existingTexindices); texture.GenerateTextureForUV(bGlobalSeamLeveling, bLocalSeamLeveling, nTextureSizeMultiple,
nRectPackingHeuristic, colEmpty, fSharpnessWeight, maxTextureSize,
baseFileName, bOriginFaceview, this, existingTexcoords, existingTexindices);
// 保存生成的纹理数据 // 保存生成的纹理数据
Mesh::Image8U3Arr existingTextures = texture.texturesDiffuseTemp; Mesh::Image8U3Arr existingTextures = texture.texturesDiffuseTemp;
// Mesh::TexCoordArr existingTexcoords = mesh.faceTexcoords; // 保存临时计算的UV
// Mesh::TexIndexArr existingTexindices = mesh.faceTexindices; // 保存纹理索引
VERBOSE("1faceTexcoords.size=%d, faces.size=%d", mesh.faceTexcoords.size(), mesh.faces.size() * 3); VERBOSE("1faceTexcoords.size=%d, faces.size=%d", mesh.faceTexcoords.size(), mesh.faces.size() * 3);
// 使用预计算UV模式
if (!mesh.Load(MAKE_PATH_SAFE(strUVMeshFileName), true)) { // 2. 使用预计算UV模式加载网格
VERBOSE("error: cannot load mesh file with UV coordinates"); if (!mesh.Load(MAKE_PATH_SAFE(strUVMeshFileName), true)) {
return false; // 注意:在成员函数中,返回 false 表示失败 VERBOSE("error: cannot load mesh file with UV coordinates");
} return false;
}
VERBOSE("2faceTexcoords.size=%d, faces.size=%d", mesh.faceTexcoords.size(), mesh.faces.size() * 3); VERBOSE("2faceTexcoords.size=%d, faces.size=%d", mesh.faceTexcoords.size(), mesh.faces.size() * 3);
// mesh.CheckUVValid();
//*
// 确保网格包含UV坐标
if (mesh.faceTexcoords.empty()) {
VERBOSE("error: the specified mesh does not contain UV coordinates");
return false;
}
// 使用新的纹理生成方法 if (mesh.faceTexcoords.empty()) {
MeshTexture texture2(*this, nResolutionLevel, nMinResolution); VERBOSE("error: the specified mesh does not contain UV coordinates");
if (!texture2.TextureWithExistingUV(views, nIgnoreMaskLabel, fOutlierThreshold, nTextureSizeMultiple, colEmpty, fSharpnessWeight, existingTextures, existingTexcoords, existingTexindices)){ return false;
return false; }
}
//*/
return true; // 3. 使用新的纹理生成方法
} MeshTexture texture2(*this, nResolutionLevel, nMinResolution);
// 关键:确保几何信息正确
texture2.scene.mesh.vertices = mesh.vertices;
texture2.scene.mesh.faces = mesh.faces;
texture2.scene.mesh.faceTexcoords = mesh.faceTexcoords; // 使用外部UV
if (!texture2.TextureWithExistingUV(views, nIgnoreMaskLabel, fOutlierThreshold,
nTextureSizeMultiple, colEmpty, fSharpnessWeight,
existingTextures, existingTexcoords, existingTexindices)) {
return false;
}
// 4. 将生成的纹理赋值回场景网格
mesh.texturesDiffuse = std::move(texture2.scene.mesh.texturesDiffuse);
mesh.faceTexindices = std::move(texture2.scene.mesh.faceTexindices);
return true;
}
// mesh.CheckUVValid(); // mesh.CheckUVValid();

Loading…
Cancel
Save