添加清晰化功能
This commit is contained in:
1 parent
5229b4f284
commit
6922abe692
1 file changed
+217
-53
+217
-53
@@ -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,
|
||||
bool SelectBestViewForTexel(const Point3f& worldPos,
|
||||
const Normal& /*normal*/,
|
||||
const std::vector<IIndex>& candidateViews,
|
||||
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);
|
||||
|
||||
@@ -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)))
|
||||
continue;
|
||||
|
||||
// 严格的投影验证
|
||||
if (!img.image.isInside(proj))
|
||||
continue;
|
||||
if (!img.camera.IsInFront(worldPos))
|
||||
continue;
|
||||
if (!ValidateProjection(worldPos, img, proj))
|
||||
continue;
|
||||
|
||||
// ✅ 先不选“最佳”,先选“第一个能用的”
|
||||
result.best_weight = 1.0f;
|
||||
result.best_view_id = viewId;
|
||||
result.best_proj = proj;
|
||||
return true;
|
||||
const float w = viewWeights[i];
|
||||
if (w <= 0.1f)
|
||||
continue;
|
||||
|
||||
candidates.push_back({viewId, proj, w});
|
||||
}
|
||||
|
||||
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(
|
||||
const VirtualFaceMap& virtualFaceMap,
|
||||
const VirtualFaceDataArr& virtualFaceDatas, // 这个参数现在不被使用,但保留以保持接口兼容
|
||||
@@ -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,34 +15637,62 @@ 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);
|
||||
// 缓存列数和数据指针
|
||||
const int cols = textureAtlas.cols;
|
||||
Pixel8U* data = reinterpret_cast<Pixel8U*>(textureAtlas.data);
|
||||
|
||||
DEBUG_EXTRA("Texture atlas size: %dx%d, UV bounds: [%.3f,%.3f]-[%.3f,%.3f]",
|
||||
textureSize, textureSize,
|
||||
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,38 +15714,33 @@ 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);
|
||||
Pixel8U color = SampleImageBicubic(bestImg.image, proj);
|
||||
|
||||
// // ✅ 正确、官方、颜色100%一致的写法
|
||||
// Pixel8U color;
|
||||
// bestImg.image.sample(
|
||||
// viewInfo.best_proj.x,
|
||||
// viewInfo.best_proj.y,
|
||||
// color,
|
||||
// SEACAVE::IMAGE_SAMPLE_BICUBIC
|
||||
// );
|
||||
// ✅ 检查是否为空色(避免写入无效像素)
|
||||
if (color[0] == colEmpty.r &&
|
||||
color[1] == colEmpty.g &&
|
||||
color[2] == colEmpty.b)
|
||||
continue;
|
||||
|
||||
data[y * cols + x] = color;
|
||||
// ✅ 写入纹理(无锁,后写覆盖先写)
|
||||
data[y * cols + x] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
Reference in new issue
Block a user