Browse Source

添加清晰化功能

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

258
libs/MVS/SceneTexture.cpp

@ -676,17 +676,27 @@ public: @@ -676,17 +676,27 @@ public:
bool CheckUVContinuity(const std::vector<FIndex>& faceList);
struct TexelViewInfo {
IIndex best_view_id; // 最佳视图ID
float best_weight; // 最佳权重
Point2f best_proj; // 最佳投影坐标
std::vector<IIndex> candidate_views; // 候选视图列表
float best_weight;
IIndex best_view_id;
Point2f best_proj;
};
bool SelectBestViewForTexel(const Point3f& worldPos,
const Normal& normal,
const Normal& /*normal*/,
const std::vector<IIndex>& candidateViews,
const std::vector<float>& viewWeights,
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);
@ -15241,40 +15251,125 @@ float MeshTexture::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,
const Normal& /*normal*/,
const std::vector<IIndex>& candidateViews,
const std::vector<float>& /*viewWeights*/,
TexelViewInfo& result)
const std::vector<float>& viewWeights,
TexelViewInfo& result,
std::vector<std::pair<IIndex, Point2f>>& topViews)
{
topViews.clear();
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) {
const IIndex viewId = candidateViews[i];
if (viewId >= images.size())
continue;
const Image& img = images[viewId];
// ✅ 和 V1 完全一致
Point2f proj = ProjectPointWithAutoCorrection(
img.camera,
Vertex(worldPos.x, worldPos.y, worldPos.z),
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;
if (!img.image.isInside(proj))
const float w = viewWeights[i];
if (w <= 0.1f)
continue;
// ✅ 先不选“最佳”,先选“第一个能用的”
result.best_weight = 1.0f;
result.best_view_id = viewId;
result.best_proj = proj;
return true;
candidates.push_back({viewId, proj, w});
}
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(
const VirtualFaceMap& virtualFaceMap,
const VirtualFaceDataArr& virtualFaceDatas, // 这个参数现在不被使用,但保留以保持接口兼容
@ -15516,16 +15611,16 @@ Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces( @@ -15516,16 +15611,16 @@ Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces(
DEBUG_EXTRA("Multi-view texture atlas generation with virtual faces complete");
return textures;
}
*/
//*
//*/
/*
Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces(
const VirtualFaceMap& virtualFaceMap,
const VirtualFaceDataArr& virtualFaceDatas,
const VirtualFaceDataArr&,
const std::vector<std::vector<IIndex>>& faceViews,
const std::vector<std::vector<float>>& faceViewWeights,
unsigned nTextureSizeMultiple,
Pixel8U colEmpty,
float fSharpnessWeight)
float)
{
DEBUG_EXTRA("Generating multi-view texture atlas with virtual faces");
@ -15542,12 +15637,12 @@ Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces( @@ -15542,12 +15637,12 @@ Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces(
const int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple);
// 3. 创建纹理图集(只做这一件事)
// 3. 创建纹理图集
Mesh::Image8U3Arr textures;
Image8U3& textureAtlas = textures.emplace_back(textureSize, textureSize);
textureAtlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r));
// 缓存列数和数据指针(性能关键)
// 缓存列数和数据指针
const int cols = textureAtlas.cols;
Pixel8U* data = reinterpret_cast<Pixel8U*>(textureAtlas.data);
@ -15556,20 +15651,48 @@ Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces( @@ -15556,20 +15651,48 @@ Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces(
uvBounds.ptMin.x(), uvBounds.ptMin.y(),
uvBounds.ptMax.x(), uvBounds.ptMax.y());
// 4. 遍历虚拟面(OpenMP安全,因为只写不读
// 4. 遍历虚拟面(OpenMP并行
#ifdef _USE_OPENMP
#pragma omp parallel for schedule(dynamic)
#endif
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];
if (faceViews[idxVF].empty()) continue;
if (faceViews[idxVF].empty())
continue;
for (FIndex faceID : vf.faces) {
// 防御性检查faceID
if (faceID >= scene.mesh.faces.size())
continue;
const Face& face = scene.mesh.faces[faceID];
const TexCoord* uv = &scene.mesh.faceTexcoords[faceID * 3];
const Vertex* vtx = &scene.mesh.vertices[face[0]];
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边界框
AABB2f uvBox(true);
uvBox.InsertFull(uv[0]);
@ -15591,37 +15714,32 @@ Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces( @@ -15591,37 +15714,32 @@ Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces(
if (!PointInTriangle(texCoord, uv[0], uv[1], uv[2], bary))
continue;
// 计算世界点(直接插值,O(1))
// 计算世界点
Point3d worldPos(
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].z * bary.x + vtx[1].z * bary.y + vtx[2].z * bary.z
);
// 逐像素选图
TexelViewInfo viewInfo;
if (!SelectBestViewForTexel(
Point3f(worldPos.x, worldPos.y, worldPos.z),
faceNormal,
faceViews[idxVF],
faceViewWeights[idxVF],
viewInfo))
// ✅ 只投影,不再选视图(使用面片选定的视图)
Point2f proj = ProjectPointWithAutoCorrection(
bestImg.camera,
Vertex(worldPos.x, worldPos.y, worldPos.z),
bestImg
);
if (!bestImg.image.isInside(proj))
continue;
// 采样并写入
const Image& bestImg = images[viewInfo.best_view_id];
// 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
// );
Pixel8U color = SampleImageBicubic(bestImg.image, proj);
// ✅ 检查是否为空色(避免写入无效像素)
if (color[0] == colEmpty.r &&
color[1] == colEmpty.g &&
color[2] == colEmpty.b)
continue;
// ✅ 写入纹理(无锁,后写覆盖先写)
data[y * cols + x] = color;
}
}
@ -15631,6 +15749,52 @@ Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces( @@ -15631,6 +15749,52 @@ Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlasWithVirtualFaces(
DEBUG_EXTRA("Multi-view texture atlas generation with virtual faces complete");
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(

Loading…
Cancel
Save