Browse Source

添加清晰化功能

ManualUV
hesuicong 4 weeks ago
parent
commit
6922abe692
  1. 272
      libs/MVS/SceneTexture.cpp

272
libs/MVS/SceneTexture.cpp

@ -676,17 +676,27 @@ public:
bool CheckUVContinuity(const std::vector<FIndex>& faceList); bool CheckUVContinuity(const std::vector<FIndex>& faceList);
struct TexelViewInfo { struct TexelViewInfo {
IIndex best_view_id; // 最佳视图ID float best_weight;
float best_weight; // 最佳权重 IIndex best_view_id;
Point2f best_proj; // 最佳投影坐标 Point2f best_proj;
std::vector<IIndex> candidate_views; // 候选视图列表
}; };
bool SelectBestViewForTexel(const Point3f& worldPos, bool SelectBestViewForTexel(const Point3f& worldPos,
const Normal& normal, const Normal& /*normal*/,
const std::vector<IIndex>& candidateViews, const std::vector<IIndex>& candidateViews,
const std::vector<float>& viewWeights, const std::vector<float>& viewWeights,
TexelViewInfo& result); TexelViewInfo& result,
std::vector<std::pair<IIndex, Point2f>>& topViews);
bool SelectBestSingleView(
const Point3f& worldPos,
const Normal& /*normal*/,
const std::vector<IIndex>& candidateViews,
const std::vector<float>& viewWeights,
TexelViewInfo& result);
Pixel8U BlendTopTwoViews(
const std::vector<std::pair<IIndex, Point2f>>& topViews,
const std::vector<IIndex>& candidateViews,
const std::vector<float>& viewWeights);
float CalculateViewScale(const Camera& cam, const Point3d& pos); float CalculateViewScale(const Camera& cam, const Point3d& pos);
@ -15241,40 +15251,125 @@ float MeshTexture::CalculateViewScale(const Camera& cam, const Point3d& pos) {
bool MeshTexture::SelectBestViewForTexel(const Point3f& worldPos, bool MeshTexture::SelectBestViewForTexel(const Point3f& worldPos,
const Normal& /*normal*/, const Normal& /*normal*/,
const std::vector<IIndex>& candidateViews, const std::vector<IIndex>& candidateViews,
const std::vector<float>& /*viewWeights*/, const std::vector<float>& viewWeights,
TexelViewInfo& result) TexelViewInfo& result,
std::vector<std::pair<IIndex, Point2f>>& topViews)
{ {
topViews.clear();
result.best_weight = -1.0f; result.best_weight = -1.0f;
result.best_view_id = static_cast<IIndex>(-1);
struct CandidateView {
IIndex viewId;
Point2f proj;
float weight;
};
std::vector<CandidateView> candidates;
candidates.reserve(candidateViews.size());
// 收集所有有效候选视图
for (size_t i = 0; i < candidateViews.size(); ++i) { for (size_t i = 0; i < candidateViews.size(); ++i) {
const IIndex viewId = candidateViews[i]; const IIndex viewId = candidateViews[i];
if (viewId >= images.size())
continue;
const Image& img = images[viewId]; const Image& img = images[viewId];
// ✅ 和 V1 完全一致
Point2f proj = ProjectPointWithAutoCorrection( Point2f proj = ProjectPointWithAutoCorrection(
img.camera, img.camera,
Vertex(worldPos.x, worldPos.y, worldPos.z), Vertex(worldPos.x, worldPos.y, worldPos.z),
img img
); );
// ✅ 只用最基本、最安全的检查 // 严格的投影验证
if (!img.camera.IsInFront(Vertex(worldPos.x, worldPos.y, worldPos.z))) if (!img.image.isInside(proj))
continue;
if (!img.camera.IsInFront(worldPos))
continue;
if (!ValidateProjection(worldPos, img, proj))
continue; continue;
if (!img.image.isInside(proj)) const float w = viewWeights[i];
if (w <= 0.1f)
continue; continue;
// ✅ 先不选“最佳”,先选“第一个能用的” candidates.push_back({viewId, proj, w});
result.best_weight = 1.0f;
result.best_view_id = viewId;
result.best_proj = proj;
return true;
} }
return false; if (candidates.empty())
return false;
// 按权重降序排序
std::sort(candidates.begin(), candidates.end(),
[](const CandidateView& a, const CandidateView& b) {
return a.weight > b.weight;
});
// 取前两个(如果有两个的话)
const int numViews = std::min(2, (int)candidates.size());
for (int i = 0; i < numViews; ++i) {
topViews.emplace_back(candidates[i].viewId, candidates[i].proj);
}
// 设置最佳视图信息
result.best_view_id = candidates[0].viewId;
result.best_proj = candidates[0].proj;
result.best_weight = candidates[0].weight;
return true;
} }
/* Pixel8U MeshTexture::BlendTopTwoViews(
const std::vector<std::pair<IIndex, Point2f>>& topViews,
const std::vector<IIndex>& candidateViews,
const std::vector<float>& viewWeights)
{
if (topViews.empty())
return Pixel8U(0, 0, 0);
// 单视图情况
if (topViews.size() == 1) {
const Image& img = images[topViews[0].first];
return SampleImageBicubic(img.image, topViews[0].second);
}
// 双视图融合
double sumB = 0, sumG = 0, sumR = 0;
float totalWeight = 0.0f;
for (const auto& view : topViews) {
const Image& img = images[view.first];
Pixel8U color = SampleImageBicubic(img.image, view.second);
// 查找对应权重
float w = 0.0f;
for (size_t i = 0; i < candidateViews.size(); ++i) {
if (candidateViews[i] == view.first) {
w = viewWeights[i];
break;
}
}
if (w <= 0.0f)
continue;
sumB += color[0] * w;
sumG += color[1] * w;
sumR += color[2] * w;
totalWeight += w;
}
if (totalWeight < 1e-6f)
return Pixel8U(0, 0, 0);
Pixel8U result;
result[0] = cv::saturate_cast<uchar>(sumB / totalWeight);
result[1] = cv::saturate_cast<uchar>(sumG / totalWeight);
result[2] = cv::saturate_cast<uchar>(sumR / totalWeight);
return result;
}
//*
Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces( Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces(
const VirtualFaceMap& virtualFaceMap, const VirtualFaceMap& virtualFaceMap,
const VirtualFaceDataArr& virtualFaceDatas, // 这个参数现在不被使用,但保留以保持接口兼容 const VirtualFaceDataArr& virtualFaceDatas, // 这个参数现在不被使用,但保留以保持接口兼容
@ -15516,16 +15611,16 @@ Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces(
DEBUG_EXTRA("Multi-view texture atlas generation with virtual faces complete"); DEBUG_EXTRA("Multi-view texture atlas generation with virtual faces complete");
return textures; return textures;
} }
*/ //*/
//* /*
Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces( Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces(
const VirtualFaceMap& virtualFaceMap, const VirtualFaceMap& virtualFaceMap,
const VirtualFaceDataArr& virtualFaceDatas, const VirtualFaceDataArr&,
const std::vector<std::vector<IIndex>>& faceViews, const std::vector<std::vector<IIndex>>& faceViews,
const std::vector<std::vector<float>>& faceViewWeights, const std::vector<std::vector<float>>& faceViewWeights,
unsigned nTextureSizeMultiple, unsigned nTextureSizeMultiple,
Pixel8U colEmpty, Pixel8U colEmpty,
float fSharpnessWeight) float)
{ {
DEBUG_EXTRA("Generating multi-view texture atlas with virtual faces"); DEBUG_EXTRA("Generating multi-view texture atlas with virtual faces");
@ -15542,34 +15637,62 @@ Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces(
const int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple); const int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple);
// 3. 创建纹理图集(只做这一件事) // 3. 创建纹理图集
Mesh::Image8U3Arr textures; Mesh::Image8U3Arr textures;
Image8U3& textureAtlas = textures.emplace_back(textureSize, textureSize); Image8U3& textureAtlas = textures.emplace_back(textureSize, textureSize);
textureAtlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); textureAtlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r));
// 缓存列数和数据指针(性能关键) // 缓存列数和数据指针
const int cols = textureAtlas.cols; const int cols = textureAtlas.cols;
Pixel8U* data = reinterpret_cast<Pixel8U*>(textureAtlas.data); Pixel8U* data = reinterpret_cast<Pixel8U*>(textureAtlas.data);
DEBUG_EXTRA("Texture atlas size: %dx%d, UV bounds: [%.3f,%.3f]-[%.3f,%.3f]", DEBUG_EXTRA("Texture atlas size: %dx%d, UV bounds: [%.3f,%.3f]-[%.3f,%.3f]",
textureSize, textureSize, textureSize, textureSize,
uvBounds.ptMin.x(), uvBounds.ptMin.y(), uvBounds.ptMin.x(), uvBounds.ptMin.y(),
uvBounds.ptMax.x(), uvBounds.ptMax.y()); uvBounds.ptMax.x(), uvBounds.ptMax.y());
// 4. 遍历虚拟面(OpenMP安全,因为只写不读 // 4. 遍历虚拟面(OpenMP并行
#ifdef _USE_OPENMP #ifdef _USE_OPENMP
#pragma omp parallel for schedule(dynamic) #pragma omp parallel for schedule(dynamic)
#endif #endif
for (int_t idxVF = 0; idxVF < (int_t)virtualFaceMap.size(); ++idxVF) { for (int_t idxVF = 0; idxVF < (int_t)virtualFaceMap.size(); ++idxVF) {
// 边界检查(防御性编程)
if (idxVF >= (int_t)faceViews.size() || idxVF >= (int_t)faceViewWeights.size())
continue;
const VirtualFace& vf = virtualFaceMap[idxVF]; const VirtualFace& vf = virtualFaceMap[idxVF];
if (faceViews[idxVF].empty()) continue; if (faceViews[idxVF].empty())
continue;
for (FIndex faceID : vf.faces) { for (FIndex faceID : vf.faces) {
// 防御性检查faceID
if (faceID >= scene.mesh.faces.size())
continue;
const Face& face = scene.mesh.faces[faceID]; const Face& face = scene.mesh.faces[faceID];
const TexCoord* uv = &scene.mesh.faceTexcoords[faceID * 3]; const TexCoord* uv = &scene.mesh.faceTexcoords[faceID * 3];
const Vertex* vtx = &scene.mesh.vertices[face[0]]; const Vertex* vtx = &scene.mesh.vertices[face[0]];
const Normal& faceNormal = scene.mesh.faceNormals[faceID]; const Normal& faceNormal = scene.mesh.faceNormals[faceID];
// ✅ 关键:用面片中心选择最佳视图(面片级选视图)
Point3d faceCenter(
(vtx[0].x + vtx[1].x + vtx[2].x) / 3.0,
(vtx[0].y + vtx[1].y + vtx[2].y) / 3.0,
(vtx[0].z + vtx[1].z + vtx[2].z) / 3.0
);
TexelViewInfo viewInfo;
if (!SelectBestSingleView(
Point3f(faceCenter.x, faceCenter.y, faceCenter.z),
faceNormal,
faceViews[idxVF],
faceViewWeights[idxVF],
viewInfo)) {
continue;
}
const Image& bestImg = images[viewInfo.best_view_id];
// UV边界框 // UV边界框
AABB2f uvBox(true); AABB2f uvBox(true);
uvBox.InsertFull(uv[0]); uvBox.InsertFull(uv[0]);
@ -15591,38 +15714,33 @@ Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces(
if (!PointInTriangle(texCoord, uv[0], uv[1], uv[2], bary)) if (!PointInTriangle(texCoord, uv[0], uv[1], uv[2], bary))
continue; continue;
// 计算世界点(直接插值,O(1)) // 计算世界点
Point3d worldPos( Point3d worldPos(
vtx[0].x * bary.x + vtx[1].x * bary.y + vtx[2].x * bary.z, vtx[0].x * bary.x + vtx[1].x * bary.y + vtx[2].x * bary.z,
vtx[0].y * bary.x + vtx[1].y * bary.y + vtx[2].y * bary.z, vtx[0].y * bary.x + vtx[1].y * bary.y + vtx[2].y * bary.z,
vtx[0].z * bary.x + vtx[1].z * bary.y + vtx[2].z * bary.z vtx[0].z * bary.x + vtx[1].z * bary.y + vtx[2].z * bary.z
); );
// 逐像素选图 // ✅ 只投影,不再选视图(使用面片选定的视图)
TexelViewInfo viewInfo; Point2f proj = ProjectPointWithAutoCorrection(
if (!SelectBestViewForTexel( bestImg.camera,
Point3f(worldPos.x, worldPos.y, worldPos.z), Vertex(worldPos.x, worldPos.y, worldPos.z),
faceNormal, bestImg
faceViews[idxVF], );
faceViewWeights[idxVF],
viewInfo)) if (!bestImg.image.isInside(proj))
continue;
Pixel8U color = SampleImageBicubic(bestImg.image, proj);
// ✅ 检查是否为空色(避免写入无效像素)
if (color[0] == colEmpty.r &&
color[1] == colEmpty.g &&
color[2] == colEmpty.b)
continue; continue;
// 采样并写入 // ✅ 写入纹理(无锁,后写覆盖先写)
const Image& bestImg = images[viewInfo.best_view_id]; data[y * cols + x] = color;
// Pixel8U color = SampleImageBicubic(bestImg.image, viewInfo.best_proj);
Pixel8U color = SampleImageBilinear(bestImg.image, viewInfo.best_proj);
// // ✅ 正确、官方、颜色100%一致的写法
// Pixel8U color;
// bestImg.image.sample(
// viewInfo.best_proj.x,
// viewInfo.best_proj.y,
// color,
// SEACAVE::IMAGE_SAMPLE_BICUBIC
// );
data[y * cols + x] = color;
} }
} }
} }
@ -15631,6 +15749,52 @@ Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces(
DEBUG_EXTRA("Multi-view texture atlas generation with virtual faces complete"); DEBUG_EXTRA("Multi-view texture atlas generation with virtual faces complete");
return textures; return textures;
} }
// ✅ 单视图选择函数(面片级)
bool MeshTexture::SelectBestSingleView(
const Point3f& worldPos,
const Normal&,
const std::vector<IIndex>& candidateViews,
const std::vector<float>& viewWeights,
TexelViewInfo& result)
{
result.best_weight = -1.0f;
result.best_view_id = static_cast<IIndex>(-1);
for (size_t i = 0; i < candidateViews.size(); ++i) {
const IIndex viewId = candidateViews[i];
if (viewId >= images.size())
continue;
const Image& img = images[viewId];
Point2f proj = ProjectPointWithAutoCorrection(
img.camera,
Vertex(worldPos.x, worldPos.y, worldPos.z),
img
);
// 严格的投影验证
if (!img.image.isInside(proj))
continue;
if (!img.camera.IsInFront(worldPos))
continue;
if (!ValidateProjection(worldPos, img, proj))
continue;
const float w = viewWeights[i];
if (w <= 0.1f)
continue;
if (w > result.best_weight) {
result.best_weight = w;
result.best_view_id = viewId;
result.best_proj = proj;
}
}
return result.best_view_id != static_cast<IIndex>(-1);
}
//*/ //*/
bool MeshTexture::TextureWithExistingUV( bool MeshTexture::TextureWithExistingUV(

Loading…
Cancel
Save