Compare commits

...
Author SHA1 Message Date
hesuicong 03929e5b95 视图排序优化 2026-05-28 18:13:42 +08:00
hesuicong 1086eb09c5 优化染色版本 2026-05-28 10:54:42 +08:00
hesuicong 7d0487ea30 针对小黄点过多和手指染色问题的参数优化 2026-05-25 14:40:16 +08:00
hesuicong cbe065a6a1 参数进一步优化 2026-05-22 17:42:12 +08:00
hesuicong 488f5a3f93 颜色溢出进一步优化 2026-05-21 14:57:14 +08:00
hesuicong e18f91e894 颜色溢出优化 2026-05-20 17:50:50 +08:00
hesuicong eafae796c2 删除保留的函数,简化 2026-04-16 09:16:33 +08:00
hesuicong 35ac5deff9 参数优化 2026-04-14 14:47:15 +08:00
hesuicong f9e7f3f18b 解决线上问题 2026-04-11 17:59:47 +08:00
hesuicong d035a20e91 参数优化 2026-04-03 10:24:05 +08:00
hesuicong d11d956b58 参数优化 2026-04-01 16:39:20 +08:00
hesuicong 743835d1b0 参数优化 2026-03-27 17:41:10 +08:00
hesuicong 4968fcc954 优化参数 2026-03-27 11:40:58 +08:00
hesuicong dbe42abf1f 优化脸色发青问题 2026-03-26 17:28:05 +08:00
hesuicong 5685a46331 处理脸色发青问题 2026-03-24 16:47:59 +08:00
hesuicong bbddc87ccc 优化过滤算法 2026-03-23 11:05:51 +08:00
hesuicong 634590e6d4 处理脸部青黄问题 2026-03-21 15:58:56 +08:00
hesuicong 955419e110 参数优化 2026-03-19 16:43:04 +08:00
hesuicong 1483610ed5 优化参数 2026-03-16 09:39:00 +08:00
hesuicong 6bd6b27f17 鼻子黑 2026-03-11 18:16:15 +08:00
hesuicong 37ebcdf0fd 鼻子黑的问题优化 2026-03-11 18:08:17 +08:00
hesuicong ef09d2521a 白痕优化 2026-03-06 17:05:01 +08:00
hesuicong 60f024abaf 优化性能 2026-03-03 16:02:18 +08:00
hesuicong 469f3df88d 优化性能 2026-03-03 16:01:18 +08:00
hesuicong bc461bd595 添加调试单视图信息 2026-02-24 17:43:34 +08:00
3 changed files with 602 additions and 376 deletions

No files matched your search

+3
View File
@@ -65,6 +65,7 @@ public:
std::map<std::string, std::unordered_set<int>> visible_faces_map;
std::map<std::string, std::unordered_set<int>> edge_faces_map;
std::map<std::string, std::unordered_set<int>> delete_edge_faces_map;
std::map<std::string, std::unordered_set<int>> face_normal_visible_map;
std::unordered_set<int> face_visible_relative;
public:
@@ -163,6 +164,7 @@ public:
std::unordered_set<int>& face_visible_relative,
std::map<std::string, std::unordered_set<int>>& edge_faces_map,
std::map<std::string, std::unordered_set<int>>& delete_edge_faces_map,
std::map<std::string, std::unordered_set<int>>& face_normal_visible_map,
std::string& basePath);
// Mesh texturing
@@ -176,6 +178,7 @@ public:
bool is_face_visible_relative(int face_index);
bool is_face_edge(const std::string& image_name, int face_index);
bool is_face_delete_edge(const std::string& image_name, int face_index);
bool is_face_normal_visible_map(const std::string& image_name, int face_index);
void SegmentMeshBasedOnCurvature(Mesh::FaceIdxArr& regionMap, float curvatureThreshold);
+500 -223
View File
@@ -461,6 +461,7 @@ public:
bool CreateVirtualFaces6(FaceDataViewArr& facesDatas, FaceDataViewArr& ComputeAverageQuality, VirtualFaceIdxsArr& virtualFaces, std::vector<bool>& isVirtualFace, unsigned minCommonCameras=2, float thMaxNormalDeviation=25.f) const;
bool CreateVirtualFaces61(FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, std::vector<bool>& isVirtualFace, unsigned minCommonCameras=2, float thMaxNormalDeviation=25.f);
bool CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataViewArr& ComputeAverageQuality, VirtualFaceIdxsArr& virtualFaces, std::vector<bool>& isVirtualFace, unsigned minCommonCameras=2, float thMaxNormalDeviation=25.f) const;
static std::string GetFileNameWithoutExtension(std::string& strPath);
float CalculateBrightnessScore(const Image& imageData) const;
bool IsFaceVisibleRelaxed(const FaceDataArr& faceDatas, const IIndexArr& selectedCams, float visibleRatioThreshold) const;
bool CheckColorConsistency(const FaceDataArr& centerDatas, const FaceDataArr& candidateDatas) const;
@@ -475,6 +476,8 @@ public:
bool CreateVirtualFaces7(FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, std::vector<bool>& isVirtualFace, unsigned minCommonCameras=2, float thMaxNormalDeviation=25.f) const;
IIndexArr SelectBestViews(const FaceDataArr& faceDatas, FIndex fid, unsigned minCommonCameras, float ratioAngleToQuality) const;
IIndexArr SelectBestViews2(const FaceDataArr& faceDatas, FIndex fid, unsigned minCommonCameras, float ratioAngleToQuality, FaceDataViewArr& facesDatas) const;
IIndexArr SelectBestView(const FaceDataArr& faceDatas, FIndex fid, unsigned minCommonCameras, float ratioAngleToQuality) const;
bool FaceViewSelection(unsigned minCommonCameras, float fOutlierThreshold, float fRatioDataSmoothness, int nIgnoreMaskLabel, const IIndexArr& views);
@@ -793,32 +796,107 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
Mesh::Octree octree;
Mesh::FacesInserter::CreateOctree(octree, scene.mesh);
#ifdef TEXOPT_USE_OPENMP
#pragma omp critical(invalid_faces_access)
#endif
{
// scene.mesh.invalidFaces.clear();
}
// #ifdef TEXOPT_USE_OPENMP
// #pragma omp critical(invalid_faces_access)
// #endif
// {
// // scene.mesh.invalidFaces.clear();
// }
std::vector<int> faceViewCount(faces.size(), 0);
// extract array of faces viewed by each image
IIndexArr views(_views);
if (views.empty()) {
views.resize(images.size());
std::iota(views.begin(), views.end(), IIndex(0));
}
Util::Progress progress(_T("Initialized views"), views.size());
FaceMap faceMap;
DepthMap depthMap;
// Temp
/*
Util::Progress progress1(_T("Initialized views"), views.size());
for (IIndex idxView: views) {
Image& imageData = images[idxView];
if (!imageData.IsValid()) {
++progress;
continue;
}
std::string strPath = imageData.name;
size_t lastSlash = strPath.find_last_of("/\\");
if (lastSlash == std::string::npos) lastSlash = 0;
else lastSlash++;
size_t lastDot = strPath.find_last_of('.');
if (lastDot == std::string::npos) lastDot = strPath.size();
std::string strName = strPath.substr(lastSlash, lastDot - lastSlash);
// load image
unsigned level(nResolutionLevel);
const unsigned imageSize(imageData.RecomputeMaxResolution(level, nMinResolution));
if ((imageData.image.empty() || MAXF(imageData.width,imageData.height) != imageSize) && !imageData.ReloadImage(imageSize)) {
return false;
}
imageData.UpdateCamera(scene.platforms);
// select faces inside view frustum
Mesh::FaceIdxArr cameraFaces;
Mesh::FacesInserter inserter(cameraFaces);
const TFrustum<float,5> frustum(Matrix3x4f(imageData.camera.P), (float)imageData.width, (float)imageData.height);
octree.Traverse(frustum, inserter);
// project all triangles in this view and keep the closest ones
faceMap.create(imageData.GetSize());
depthMap.create(imageData.GetSize());
RasterMesh rasterer(*this, vertices, imageData.camera, depthMap, faceMap, scene.mesh, false);
RasterMesh::Triangle triangle;
RasterMesh::TriangleRasterizer triangleRasterizer(triangle, rasterer);
// 简化后的可见性检查
for (FIndex idxFace : cameraFaces) {
rasterer.validFace = true;
const Face& facet = faces[idxFace];
rasterer.idxFace = idxFace;
// 只进行基本的可见性检查
if (scene.is_face_visible(strName.c_str(), idxFace)) {
// 尝试投影
rasterer.Project(facet, triangleRasterizer);
// 如果投影成功,增加计数
if (rasterer.validFace) {
faceViewCount[idxFace]++;
}
}
}
++progress;
}
//*/
// Temp
std::vector<std::vector<Color>> faceViewColors(faces.size()); // 存储每个面片在各个视图下的颜色
std::vector<std::vector<float>> faceViewQualities(faces.size()); // 存储每个面片在各个视图下的质量
facesDatas.resize(faces.size());
// viewDepthMaps.resize(views.size()); // 初始化深度图存储
Util::Progress progress(_T("Initialized views"), views.size());
typedef float real;
TImage<real> imageGradMag;
TImage<real>::EMat mGrad[2];
FaceMap faceMap;
DepthMap depthMap;
Util::Progress progress2(_T("Processing views"), views.size());
#ifdef TEXOPT_USE_OPENMP
bool bAbort(false);
#pragma omp parallel for private(imageGradMag, mGrad, faceMap, depthMap)
for (int_t idx=0; idx<(int_t)views.size(); ++idx) {
for (int_t idx=0; idx<(int_t)views.size(); ++idx)
{
#pragma omp flush (bAbort)
if (bAbort) {
++progress;
@@ -855,8 +933,28 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
// strName!="92_8" && strName!="101_8" && strName!="102_8" &&
// strName!="103_8" && strName!="112_8" && strName!="113_8" &&
// strName!="122_2" && strName!="123_2" && strName!="121_2")
// if (strName!="122_2")
if (strName!="122_2" && strName!="123_2" && strName!="121_2")
// if (strName!="24_8")
if (
strName!="12_8" && strName!="15_8"
// && strName!="21_8" && strName!="22_8" && strName!="23_8" && strName!="24_8" && strName!="25_8"
&& strName!="21_8" && strName!="22_8" && strName!="23_8"
&& strName!="31_8" && strName!="32_8" && strName!="33_8" && strName!="34_8" && strName!="35_8"
&& strName!="42_8" && strName!="43_8" && strName!="45_8"
&& strName!="52_8" && strName!="53_8" && strName!="54_8" && strName!="55_8"
&& strName!="61_8" && strName!="62_8" && strName!="63_8" && strName!="64_8" && strName!="65_8"
&& strName!="71_8" && strName!="73_8" && strName!="74_8" && strName!="75_8"
&& strName!="81_8" && strName!="82_8" && strName!="83_8" && strName!="84_8" && strName!="85_8"
&& strName!="91_8" && strName!="92_8" && strName!="93_8" && strName!="94_8" && strName!="95_8"
&& strName!="101_8" && strName!="102_8" && strName!="103_8" && strName!="104_8" && strName!="105_8"
&& strName!="111_8" && strName!="113_8" && strName!="114_8" && strName!="115_8"
&& strName!="121_8" && strName!="122_8" && strName!="123_8" && strName!="124_8" && strName!="125_8"
&& strName!="131_8" && strName!="132_8" && strName!="133_8" && strName!="134_8" && strName!="135_8"
&& strName!="141_8" && strName!="142_8" && strName!="143_8" && strName!="145_8"
&& strName!="151_8" && strName!="153_8" && strName!="154_8" && strName!="155_8"
&& strName!="161_8" && strName!="162_8" && strName!="163_8" && strName!="164_8" && strName!="165_8"
&& strName!="171_8" && strName!="172_8" && strName!="173_8" && strName!="174_8" && strName!="175_8"
&& strName!="181_8" && strName!="182_8" && strName!="183_8" && strName!="184_8" && strName!="185_8"
)
{
continue;
}
@@ -907,160 +1005,7 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
depthMap.create(imageData.GetSize());
std::unordered_set<FIndex> tempFaces;
if (false)
{
// viewDepthMaps[idxView] = depthMap;
for (int r = 0; r < depthMap.rows; ++r) {
for (int c = 0; c < depthMap.cols; ++c) {
// printf("1depthMap(r, c)=%f\n", depthMap(r, c));
}
}
RasterMesh::Triangle triangle;
RasterMesh rasterer1(*this, vertices, imageData.camera, depthMap, faceMap, scene.mesh, false);
RasterMesh::TriangleRasterizer triangleRasterizer(triangle, rasterer1);
if (nIgnoreMaskLabel >= 0) {
// import mask
BitMatrix bmask;
// std::cout << "nIgnoreMaskLabel is open" << std::endl;
DepthEstimator::ImportIgnoreMask(imageData, imageData.GetSize(), (uint16_t)OPTDENSE::nIgnoreMaskLabel, bmask, &rasterer1.mask);
} else if (nIgnoreMaskLabel == -1) {
// creating mask to discard invalid regions created during image radial undistortion
rasterer1.mask = DetectInvalidImageRegions(imageData.image);
#if TD_VERBOSE != TD_VERBOSE_OFF
if (VERBOSITY_LEVEL > 2)
cv::imwrite(String::FormatString("umask%04d.png", idxView), rasterer1.mask);
#endif
}
rasterer1.Clear();
#ifdef TEXOPT_USE_OPENMP
#pragma omp critical(invalid_faces_access)
#endif
{
std::lock_guard<std::mutex> lock(*scene.mesh.invalidFaces.mtx);
scene.mesh.invalidFaces.data.clear();
}
printf("imageData.name=%s\n", imageData.name.c_str());
for (FIndex idxFace : cameraFaces) {
if (idxFace >= faces.size()) {
DEBUG_EXTRA("Invalid face index %u (max %u) in view %u", idxFace, faces.size()-1, idxView);
continue;
}
// // 添加临界区保护
// bool skipFace = false;
// #ifdef TEXOPT_USE_OPENMP
// #pragma omp critical(invalid_faces_access)
// #endif
// {
// std::lock_guard<std::mutex> lock(scene.mesh.invalidFacesMutex);
// skipFace = (scene.mesh.invalidFaces.find(idxFace) != scene.mesh.invalidFaces.end());
// }
// if (skipFace)
// {
// continue;
// }
rasterer1.validFace = true;
const Face& facet = faces[idxFace];
rasterer1.idxFace = idxFace;
if (scene.is_face_visible(strName.c_str(), idxFace))
{
rasterer1.Project(facet, triangleRasterizer);
if (!rasterer1.validFace)
rasterer1.Project(facet, triangleRasterizer);
}
}
// if (!bUseVirtualFaces)
// if (bUseVirtualFaces)
tempFaces = PerformLocalDepthConsistencyCheck(depthMap, faceMap, scene.mesh, idxView, strName);
// RasterMesh rasterer2(*this, vertices, imageData.camera, depthMap, faceMap, scene.mesh, true);
RasterMesh rasterer2(*this, vertices, imageData.camera, depthMap, faceMap, std::ref(scene.mesh), true);
for (int r = 0; r < depthMap.rows; ++r) {
for (int c = 0; c < depthMap.cols; ++c) {
// if (depthMap(r, c)> 999.0f)
// printf("2depthMap(r, c)=%f\n", depthMap(r, c));
}
}
RasterMesh::TriangleRasterizer triangleRasterizer2(triangle, rasterer2);
if (nIgnoreMaskLabel >= 0) {
// import mask
BitMatrix bmask;
// std::cout << "nIgnoreMaskLabel is open" << std::endl;
DepthEstimator::ImportIgnoreMask(imageData, imageData.GetSize(), (uint16_t)OPTDENSE::nIgnoreMaskLabel, bmask, &rasterer2.mask);
} else if (nIgnoreMaskLabel == -1) {
// creating mask to discard invalid regions created during image radial undistortion
rasterer2.mask = DetectInvalidImageRegions(imageData.image);
#if TD_VERBOSE != TD_VERBOSE_OFF
if (VERBOSITY_LEVEL > 2)
cv::imwrite(String::FormatString("umask%04d.png", idxView), rasterer2.mask);
#endif
}
for (int r = 0; r < depthMap.rows; ++r) {
for (int c = 0; c < depthMap.cols; ++c) {
// if (depthMap(r, c)> 999.0f)
// printf("3depthMap(r, c)=%f, r=%d, c=%d, faceMap(r, c)=%d\n", depthMap(r, c), r, c, faceMap(r, c));
}
}
// rasterer2.Clear();
for (FIndex idxFace : cameraFaces) {
// 添加面索引有效性检查
if (idxFace >= faces.size()) {
DEBUG_EXTRA("Invalid face index %u (max %u) in view %u", idxFace, faces.size()-1, idxView);
continue;
}
// 添加临界区保护
bool skipFace = false;
#ifdef TEXOPT_USE_OPENMP
#pragma omp critical(invalid_faces_access)
#endif
{
std::lock_guard<std::mutex> lock(*scene.mesh.invalidFaces.mtx);
skipFace = (scene.mesh.invalidFaces.data.find(idxFace) != scene.mesh.invalidFaces.data.end());
}
if (skipFace)
{
continue;
}
if (tempFaces.find(idxFace) != tempFaces.end())
{
}
else
{
continue;
}
rasterer2.validFace = true;
const Face& facet = faces[idxFace];
rasterer2.idxFace = idxFace;
// rasterer2.Project(facet, triangleRasterizer2);
// if (!rasterer2.validFace)
// rasterer2.Project(facet, triangleRasterizer2);
}
for (int r = 0; r < depthMap.rows; ++r) {
for (int c = 0; c < depthMap.cols; ++c) {
// if (depthMap(r, c)> 999.0f)
// printf("4depthMap(r, c)=%f, r=%d, c=%d, faceMap(r, c)=%d\n", depthMap(r, c), r, c, faceMap(r, c));
}
}
}
else
{
// RasterMesh rasterer(vertices, imageData.camera, depthMap, faceMap);
RasterMesh rasterer(*this, vertices, imageData.camera, depthMap, faceMap, scene.mesh, false);
@@ -1085,11 +1030,34 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
const Face& facet = faces[idxFace];
rasterer.idxFace = idxFace;
if (scene.is_face_visible(strName.c_str(), idxFace))
bool normal_visible_check = false;
int coverage = faceViewCount[idxFace];
// if (coverage<2||coverage>=8) {
// normal_visible_check = true;
// }
bool is_face_visible = scene.is_face_visible(strName.c_str(), idxFace);
bool is_face_normal_visible_map = scene.is_face_normal_visible_map(strName.c_str(), idxFace);
bool is_face_edge = scene.is_face_edge(strName.c_str(), idxFace);
bool is_face_delete_edge = scene.is_face_delete_edge(strName.c_str(), idxFace);
if (!is_face_edge)
is_face_normal_visible_map = true;
if (is_face_visible && is_face_normal_visible_map)
// if (is_face_visible)
{
rasterer.Project(facet, triangleRasterizer);
if (!rasterer.validFace)
rasterer.Project(facet, triangleRasterizer);
// 如果投影成功,增加面片覆盖率计数
// if (rasterer.validFace)
// {
// faceViewCount[idxFace]++;
// }
}
}
@@ -1130,6 +1098,12 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
// continue;
}
float brightnessPerceptual = (0.299f * pixel.r + 0.587f * pixel.g + 0.114f * pixel.b) / 255.0f;
// printf("brightnessPerceptual=%f\n", brightnessPerceptual);
if (brightnessPerceptual>0.95f)
continue;
// if (!(scene.mesh.invalidFacesRelative.data.contains(idxFace) && scene.is_face_visible_relative(idxFace)))
// if (false)
{
@@ -1208,6 +1182,13 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
if (depthMap(j,i)>999.0f)
faceData.bInvalidFacesRelative = true;
}
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
// 保存视图颜色和质量
Color pixelColor = imageData.image(j,i);
faceViewColors[idxFace].push_back(pixelColor);
faceViewQualities[idxFace].push_back(imageGradMag(j,i));
#endif
}
}
// adjust face quality with camera angle relative to face normal
@@ -1220,8 +1201,15 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
const Vertex faceCenter((vertices[f[0]] + vertices[f[1]] + vertices[f[2]]) / 3.f);
const Point3f camDir(Cast<Mesh::Type>(imageData.camera.C) - faceCenter);
const Normal& faceNormal = scene.mesh.faceNormals[idxFace];
const float cosFaceCam(MAXF(0.001f, ComputeAngle(camDir.ptr(), faceNormal.ptr())));
// const float MIN_COS_THRESHOLD = 0.1f; // 增大阈值,减少极端倾斜视角的影响
// const float cosFaceCam(MAXF(MIN_COS_THRESHOLD, ComputeAngle(camDir.ptr(), faceNormal.ptr())));
faceDatas.back().quality *= SQUARE(cosFaceCam);
// faceDatas.back().quality *= cosFaceCam; // 线性调整
// faceDatas.back().quality *= pow(cosFaceCam, 0.5f); // 平方根调整
// faceDatas.back().quality *= 1.0f - 0.5f * (1.0f - cosFaceCam); // 平滑调整
}
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
FOREACH(idxFace, areas) {
@@ -1235,6 +1223,7 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
}
++progress;
}
#ifdef TEXOPT_USE_OPENMP
if (bAbort)
return false;
@@ -1789,6 +1778,97 @@ void MeshTexture::PerformLocalDepthConsistencyCheck(DepthMap& depthMap, FaceMap&
}
//*/
IIndexArr MeshTexture::SelectBestViews2(const FaceDataArr& faceDatas, FIndex fid, unsigned minCommonCameras, float ratioAngleToQuality, FaceDataViewArr& facesDatas) const
{
ASSERT(!faceDatas.empty());
// 计算每个视图能看到的相邻面片数量
std::unordered_map<IIndex, int> viewCoverageCount;
for (const FaceData& fd : faceDatas) {
if (!fd.bInvalidFacesRelative) {
viewCoverageCount[fd.idxView] = 0;
}
}
// 统计每个视图能看到的面片数
for (const FaceData& fd : faceDatas) {
if (!fd.bInvalidFacesRelative) {
// 统计这个视图能看到的所有相邻面片
const Mesh::FaceFaces& ffaces = faceFaces[fid];
for (int i = 0; i < 3; ++i) {
const FIndex neighborIdx = ffaces[i];
if (neighborIdx != NO_ID) {
const FaceDataArr& neighborDatas = facesDatas[neighborIdx];
for (const FaceData& neighborFd : neighborDatas) {
if (neighborFd.idxView == fd.idxView && !neighborFd.bInvalidFacesRelative) {
viewCoverageCount[fd.idxView]++;
break;
}
}
}
}
}
}
// 重新计算分数,考虑覆盖范围
float maxQuality = 0;
for (const FaceData& faceData: faceDatas)
maxQuality = MAXF(maxQuality, faceData.quality);
const Face& f = faces[fid];
const Vertex faceCenter((vertices[f[0]] + vertices[f[1]] + vertices[f[2]]) / 3.f);
CLISTDEF0IDX(float,IIndex) scores(faceDatas.size());
FOREACH(idxFaceData, faceDatas) {
const FaceData& faceData = faceDatas[idxFaceData];
if (faceData.bInvalidFacesRelative) {
scores[idxFaceData] = -1.0f; // 无效视图分数为负
continue;
}
const Image& imageData = images[faceData.idxView];
const Point3f camDir(Cast<Mesh::Type>(imageData.camera.C) - faceCenter);
const Normal& faceNormal = scene.mesh.faceNormals[fid];
const float cosFaceCam(ComputeAngle(camDir.ptr(), faceNormal.ptr()));
// 计算覆盖范围分数(0-1之间)
float coverageScore = 0.0f;
if (viewCoverageCount.find(faceData.idxView) != viewCoverageCount.end()) {
int maxCoverage = 0;
for (const auto& pair : viewCoverageCount) {
maxCoverage = MAX(maxCoverage, pair.second);
}
if (maxCoverage > 0) {
coverageScore = static_cast<float>(viewCoverageCount[faceData.idxView]) / maxCoverage;
}
}
// 综合角度、质量和覆盖范围
const float angleScore = ratioAngleToQuality * cosFaceCam;
const float qualityScore = (1.0f - ratioAngleToQuality) * faceData.quality / maxQuality;
const float coverageWeight = 0.3f; // 覆盖范围权重
const float combinedScore = 0.4f * angleScore + 0.3f * qualityScore + coverageWeight * coverageScore;
scores[idxFaceData] = combinedScore;
}
// 按分数排序
IIndexArr scorePodium(faceDatas.size());
std::iota(scorePodium.begin(), scorePodium.end(), 0);
scorePodium.Sort([&scores](IIndex i, IIndex j) {
return scores[i] > scores[j];
});
// 选择最佳视图
IIndexArr cameras;
for (IIndex i = 0; i < MIN(minCommonCameras, faceDatas.size()); ++i) {
if (scores[scorePodium[i]] > 0.1f) { // 分数阈值
cameras.push_back(faceDatas[scorePodium[i]].idxView);
}
}
return cameras;
}
// order the camera view scores with highest score first and return the list of first <minCommonCameras> cameras
// ratioAngleToQuality represents the ratio in witch we combine normal angle to quality for a face to obtain the selection score
// - a ratio of 1 means only angle is considered
@@ -2975,8 +3055,8 @@ bool MeshTexture::CreateVirtualFaces6(FaceDataViewArr& facesDatas, FaceDataViewA
ASSERT(posToErase != Mesh::FaceIdxArr::NO_INDEX);
remainingFaces.RemoveAtMove(posToErase);
} else {
// IIndexArr selectedCams = SelectBestViews2(centerFaceDatas, virtualFaceCenterFaceID, minCommonCameras, ratioAngleToQuality, facesDatas);
IIndexArr selectedCams = SelectBestViews(centerFaceDatas, virtualFaceCenterFaceID, minCommonCameras, ratioAngleToQuality);
//*
// 获取中心面片的法线 (注意变量名是 normalCenter, 不是 centerNormal)
const Normal& normalCenter = scene.mesh.faceNormals[virtualFaceCenterFaceID];
@@ -3035,9 +3115,10 @@ bool MeshTexture::CreateVirtualFaces6(FaceDataViewArr& facesDatas, FaceDataViewA
if (angleDeg <= 45.0f)
{
// filteredCams.push_back(idxView);
if (scene.is_face_normal_visible_map(strName, virtualFaceCenterFaceID))
filteredCams.push_back(idxView);
//*
/*
float brightnessScore = CalculateBrightnessScore(imageData); // 亮度评分函数
float angleScore = 1.0f - (angleDeg / 45.0f);
float qualityScore = 0.0f;
@@ -3058,9 +3139,11 @@ bool MeshTexture::CreateVirtualFaces6(FaceDataViewArr& facesDatas, FaceDataViewA
}
else
{
// filteredCams.push_back(idxView);
//*
if (scene.is_face_normal_visible_map(strName, virtualFaceCenterFaceID))
filteredCams.push_back(idxView);
/*
float brightnessScore = CalculateBrightnessScore(imageData); // 亮度评分函数
float angleScore = 1.0f - (angleDeg / 60.0f);
float qualityScore = 0.0f;
@@ -3492,8 +3575,12 @@ bool MeshTexture::CreateVirtualFaces6(FaceDataViewArr& facesDatas, FaceDataViewA
#endif
// virtualFaceData.quality = avgQuality;
// virtualFaceData.color = sortedLuminViews[0].second;
virtualFaceData.quality = medianQuality;
virtualFaceData.color = medianColor;
// virtualFaceData.quality = 0;
// virtualFaceData.color = Point3f::ZERO;
}
else {
// 使用过滤后的视图重新计算平均值
@@ -3510,6 +3597,9 @@ bool MeshTexture::CreateVirtualFaces6(FaceDataViewArr& facesDatas, FaceDataViewA
}
virtualFaceData.quality = totalQuality2 / validIndices.size();
virtualFaceData.color = totalColor2 / validIndices.size();
// virtualFaceData.quality = 0;
// virtualFaceData.color = Point3f::ZERO;
}
}
else if (validViewsSize>0&&validViewsSize<=2&&false)
@@ -4185,6 +4275,35 @@ MeshTexture::FaceDataArr MeshTexture::MergeFaceData(const FaceDataArr& data1, co
return mergedData;
}
std::string MeshTexture::GetFileNameWithoutExtension(std::string& strPath) {
// 查找最后一个路径分隔符(支持'/'和'\')
size_t lastSlash = strPath.find_last_of("/\\");
// 确定文件名的起始位置
if (lastSlash == std::string::npos) {
lastSlash = 0; // 没有路径分隔符,从字符串开头开始
} else {
lastSlash++; // 跳过分隔符
}
// 查找最后一个扩展名分隔符'.'的位置
size_t lastDot = strPath.find_last_of('.');
// 如果没有扩展名分隔符,则截取到字符串末尾
if (lastDot == std::string::npos) {
lastDot = strPath.length();
}
// 截取文件名(不含路径和扩展名)
// 注意:如果lastDot <= lastSlash,说明点号在路径中,而不是在文件名中
if (lastDot <= lastSlash) {
// 点号在路径中,没有扩展名
return strPath.substr(lastSlash);
} else {
// 有扩展名,去掉扩展名
return strPath.substr(lastSlash, lastDot - lastSlash);
}
}
bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, std::vector<bool>& isVirtualFace, unsigned minCommonCameras, float thMaxNormalDeviation) const
{
@@ -4202,6 +4321,16 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
cQueue<FIndex, FIndex, 0> currentVirtualFaceQueue;
std::unordered_set<FIndex> queuedFaces;
// std::vector<std::unordered_set<IIndex>> faceValidCameras(faces.size());
// for (FIndex idxFace = 0; idxFace < faces.size(); ++idxFace) {
// const FaceDataArr& faceDatas = facesDatas[idxFace];
// for (const FaceData& fd : faceDatas) {
// if (!fd.bInvalidFacesRelative) {
// faceValidCameras[idxFace].insert(fd.idxView);
// }
// }
// }
// Precompute average color for each face
Colors faceColors; // 创建一个空列表
faceColors.reserve(faces.size()); // 预分配空间(如果cList有reserve方法且您关心性能)
@@ -4300,11 +4429,80 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
remainingFaces.RemoveAtMove(posToErase);
} else {
IIndexArr selectedCams = SelectBestViews(centerFaceDatas, virtualFaceCenterFaceID, minCommonCameras, ratioAngleToQuality);
// IIndexArr selectedCams = SelectBestViews2(centerFaceDatas, virtualFaceCenterFaceID, minCommonCameras, ratioAngleToQuality, facesDatas);
//*
// 获取中心面片的法线 (注意变量名是 normalCenter, 不是 centerNormal)
const Normal& normalCenter = scene.mesh.faceNormals[virtualFaceCenterFaceID];
// 过滤selectedCams:只保留夹角小于30度的视图
std::map<IIndex, float> mapSortedcams;
for (IIndex idxView : selectedCams)
{
const Image& imageData = images[idxView];
// 计算相机在世界坐标系中的朝向向量(相机镜面法线)
const RMatrix& R = imageData.camera.R;
// 相机局部坐标系中的向前向量 (0,0,-1)
Point3f localForward(0.0f, 0.0f, -1.0f);
// 手动计算矩阵乘法:cameraForward = R * localForward
Point3f cameraForward;
cameraForward.x = R(0,0) * localForward.x + R(0,1) * localForward.y + R(0,2) * localForward.z;
cameraForward.y = R(1,0) * localForward.x + R(1,1) * localForward.y + R(1,2) * localForward.z;
cameraForward.z = R(2,0) * localForward.x + R(2,1) * localForward.y + R(2,2) * localForward.z;
// 手动归一化 cameraForward
float norm = std::sqrt(cameraForward.x * cameraForward.x +
cameraForward.y * cameraForward.y +
cameraForward.z * cameraForward.z);
if (norm > 0.0f) {
cameraForward.x /= norm;
cameraForward.y /= norm;
cameraForward.z /= norm;
} else {
cameraForward = Point3f(0, 0, -1);
}
Point3f normalPoint(normalCenter.x, normalCenter.y, normalCenter.z);
float cosAngle = cameraForward.dot(normalPoint);
float angleDeg = std::acos(cosAngle) * 180.0f / M_PI;
std::string strPath = imageData.name;
std::string strName = MeshTexture::GetFileNameWithoutExtension(strPath);
if (!scene.is_face_delete_edge(strName, virtualFaceCenterFaceID)) {
if (scene.is_face_edge(strName, virtualFaceCenterFaceID))
{
if (angleDeg <= 40.0f)
{
mapSortedcams[idxView] = angleDeg;
}
}
else
{
mapSortedcams[idxView] = angleDeg;
}
}
}
// 将map中的元素放入vector以便排序
std::vector<std::pair<IIndex, float>> sortedCams;
sortedCams.reserve(mapSortedcams.size());
for (const auto& pair : mapSortedcams) {
sortedCams.emplace_back(pair.first, pair.second);
}
// 按angleDeg从小到大排序
std::sort(sortedCams.begin(), sortedCams.end(),
[](const std::pair<IIndex, float>& a, const std::pair<IIndex, float>& b) {
return a.second < b.second; // 按angleDeg排序
});
IIndexArr filteredCams;
size_t count = std::min(sortedCams.size(), static_cast<size_t>(3));
for (size_t i = 0; i < count; ++i) {
filteredCams.push_back(sortedCams[i].first);
}
/*
IIndexArr filteredCams; // 用于存储过滤后的视图索引
for (IIndex idxView : selectedCams) {
const Image& imageData = images[idxView];
@@ -4338,16 +4536,17 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
float angleDeg = std::acos(cosAngle) * 180.0f / M_PI; // 将弧度转换为角度
std::string strPath = imageData.name;
size_t lastSlash = strPath.find_last_of("/\\");
if (lastSlash == std::string::npos) lastSlash = 0; // 若无分隔符,从头开始
else lastSlash++; // 跳过分隔符
std::string strName = MeshTexture::GetFileNameWithoutExtension(strPath);
// size_t lastSlash = strPath.find_last_of("/\\");
// if (lastSlash == std::string::npos) lastSlash = 0; // 若无分隔符,从头开始
// else lastSlash++; // 跳过分隔符
// 查找扩展名分隔符 '.' 的位置
size_t lastDot = strPath.find_last_of('.');
if (lastDot == std::string::npos) lastDot = strPath.size(); // 若无扩展名,截到末尾
// // 查找扩展名分隔符 '.' 的位置
// size_t lastDot = strPath.find_last_of('.');
// if (lastDot == std::string::npos) lastDot = strPath.size(); // 若无扩展名,截到末尾
// 截取文件名(不含路径和扩展名)
std::string strName = strPath.substr(lastSlash, lastDot - lastSlash);
// // 截取文件名(不含路径和扩展名)
// std::string strName = strPath.substr(lastSlash, lastDot - lastSlash);
// printf("CreateVirtualFace %s, %d\n", strName.c_str(), virtualFaceCenterFaceID);
@@ -4357,53 +4556,54 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
{
// printf("CreateVirtualFace %s, %d, %f\n", strName.c_str(), virtualFaceCenterFaceID, angleLimit);
if (angleDeg <= 45.0f)
if (angleDeg <= 40.0f)
{
// filteredCams.push_back(idxView);
//*
float brightnessScore = CalculateBrightnessScore(imageData); // 亮度评分函数
float angleScore = 1.0f - (angleDeg / 45.0f);
float qualityScore = 0.0f;
const FaceDataArr& centerFaceDatas = facesDatas[virtualFaceCenterFaceID];
for (const FaceData& fd : centerFaceDatas) {
if (fd.idxView == idxView) {
qualityScore = fd.quality;
break;
}
}
qualityScore = std::max(0.0f, std::min(1.0f, qualityScore));
float overallScore = 0.5f * angleScore + 0.3f * brightnessScore + 0.2f * qualityScore;
if (overallScore > 0.04f) {
filteredCams.push_back(idxView);
}
//*/
// float brightnessScore = CalculateBrightnessScore(imageData); // 亮度评分函数
// float angleScore = 1.0f - (angleDeg / 45.0f);
// float qualityScore = 0.0f;
// const FaceDataArr& centerFaceDatas = facesDatas[virtualFaceCenterFaceID];
// for (const FaceData& fd : centerFaceDatas) {
// if (fd.idxView == idxView) {
// qualityScore = fd.quality;
// break;
// }
// }
// qualityScore = std::max(0.0f, std::min(1.0f, qualityScore));
// float overallScore = 0.5f * angleScore + 0.3f * brightnessScore + 0.2f * qualityScore;
// if (overallScore > 0.04f) {
// filteredCams.push_back(idxView);
// }
}
}
else
{
// filteredCams.push_back(idxView);
//*
float brightnessScore = CalculateBrightnessScore(imageData); // 亮度评分函数
float angleScore = 1.0f - (angleDeg / 45.0f);
float qualityScore = 0.0f;
const FaceDataArr& centerFaceDatas = facesDatas[virtualFaceCenterFaceID];
for (const FaceData& fd : centerFaceDatas) {
if (fd.idxView == idxView) {
qualityScore = fd.quality;
break;
}
}
qualityScore = std::max(0.0f, std::min(1.0f, qualityScore));
float overallScore = 0.5f * angleScore + 0.3f * brightnessScore + 0.2f * qualityScore;
if (overallScore > 0.02f) {
// if (angleDeg <= 30.0f)
{
filteredCams.push_back(idxView);
}
//*/
// float brightnessScore = CalculateBrightnessScore(imageData); // 亮度评分函数
// float angleScore = 1.0f - (angleDeg / 45.0f);
// float qualityScore = 0.0f;
// const FaceDataArr& centerFaceDatas = facesDatas[virtualFaceCenterFaceID];
// for (const FaceData& fd : centerFaceDatas) {
// if (fd.idxView == idxView) {
// qualityScore = fd.quality;
// break;
// }
// }
// qualityScore = std::max(0.0f, std::min(1.0f, qualityScore));
// float overallScore = 0.5f * angleScore + 0.3f * brightnessScore + 0.2f * qualityScore;
// if (overallScore > 0.02f) {
// filteredCams.push_back(idxView);
// }
}
}
}
*/
// 确保 selectedCams 是非 const 的,才能对其进行赋值
// 例如,其声明应为:IIndexArr selectedCams = ...; (不能是 const IIndexArr)
@@ -4426,22 +4626,40 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
do {
const FIndex currentFaceId = currentVirtualFaceQueue.GetHead();
currentVirtualFaceQueue.PopHead();
// check for condition to add in current virtual face
// normal angle smaller than thMaxNormalDeviation degrees
const Normal& faceNormal = scene.mesh.faceNormals[currentFaceId];
const float cosFaceToCenter(ComputeAngleN(normalCenter.ptr(), faceNormal.ptr()));
// if (cosFaceToCenter < cosMaxNormalDeviation)
// continue;
if (cosFaceToCenter < dynamicCosTh) // 使用动态阈值
if (cosFaceToCenter < dynamicCosTh)
continue;
// if (cosFaceToCenter < dynamicCosTh) // 使用动态阈值
// continue;
// printf("dynamicCosTh=%f\n", dynamicCosTh);
// if (cosFaceToCenter < 0.99) // 对应60度,比原来的更宽松
// continue;
// check if current face is seen by all cameras in selectedCams
ASSERT(!selectedCams.empty());
if (!IsFaceVisible(facesDatas[currentFaceId], selectedCams))
continue;
// Check color similarity
// 3. 放宽颜色差异条件
const Color& centerColor = faceColors[virtualFaceCenterFaceID];
const Color& currentColor = faceColors[currentFaceId];
float colorDistance = cv::norm(centerColor - currentColor);
if (colorDistance > 200.0f) { // 从200.0f放宽到250.0f
// continue; // 如果颜色差异太大,跳过
}
// check if current face is seen by all cameras in selectedCams
// ASSERT(!selectedCams.empty());
// if (!IsFaceVisible(facesDatas[currentFaceId], selectedCams))
// continue;
// // Check color similarity
// const Color& centerColor = faceColors[virtualFaceCenterFaceID];
// const Color& currentColor = faceColors[currentFaceId];
// if (cv::norm(centerColor) > 1e-5 && cv::norm(currentColor) > 1e-5)
{
float colorDistance = cv::norm(centerColor - currentColor);
@@ -4481,6 +4699,10 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
virtualFaceData.color = Point3f::ZERO;
#endif
const Image& imageData = images[idxView];
std::string strPath = imageData.name;
std::string strName = MeshTexture::GetFileNameWithoutExtension(strPath);
int invalidQuality = 0;
Color invalidColor = Point3f::ZERO;
unsigned processedFaces(0);
@@ -4553,6 +4775,8 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
if (colorDistance <= maxColorDeviation &&
luminanceDistance <= maxLuminanceDeviation)
{
if (scene.is_face_normal_visible_map(strName, virtualFaceCenterFaceID))
validIndices.push_back(n);
}
else
@@ -4577,7 +4801,9 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
if (cosFaceToCenter<dynamicCosTh)
{
if (nInvalidViewCount<=2)
{
validIndices.push_back(n);
}
else
{
// if ((colorDistance <= 350.0f))
@@ -4587,7 +4813,9 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
else
{
if (nInvalidViewCount<=2)
{
validIndices.push_back(n);
}
else
{
// if (bColorSimilarity)
@@ -4677,6 +4905,11 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
virtualFaceData.quality = medianQuality;
virtualFaceData.color = medianColor;
// virtualFaceData.quality = 0;
// #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
// virtualFaceData.color = Point3f::ZERO;
// #endif
}
else {
// 使用过滤后的视图重新计算平均值
@@ -4688,6 +4921,11 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
}
virtualFaceData.quality = totalQuality2 / validIndices.size();
virtualFaceData.color = totalColor2 / validIndices.size();
// virtualFaceData.quality = 0;
// #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
// virtualFaceData.color = Point3f::ZERO;
// #endif
}
}
@@ -7009,7 +7247,7 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT
CreateVirtualFaces62(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras);
TD_TIMER_STARTD();
// CreateVirtualFaces7(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras);
DEBUG_EXTRA("CreateVirtualFaces7 completed: %s", TD_TIMER_GET_FMT().c_str());
DEBUG_EXTRA("CreateVirtualFaces completed: %s", TD_TIMER_GET_FMT().c_str());
size_t controlCounter(0);
FOREACH(idxVF, virtualFaces) {
@@ -7365,12 +7603,16 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT
}
//*/
//*
// int n = 0;
// 修改后安全版本
FOREACH(l, labels) {
if (l < mapFaceToVirtualFace.size()) {
const size_t virtualIdx = mapFaceToVirtualFace[l];
if (virtualIdx < virtualLabels.size()) {
// if (n < 100)
labels[l] = virtualLabels[virtualIdx];
// else
// labels[l] = NO_ID;
} else {
labels[l] = NO_ID;
DEBUG_EXTRA("Warning: Invalid virtual face index for face %u: %u (max: %u)",
@@ -7381,6 +7623,8 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT
DEBUG_EXTRA("Warning: Face index out of bounds: %u (max: %u)",
l, mapFaceToVirtualFace.size()-1);
}
// ++n;
}
//*/
// 修改后安全版本
@@ -10335,6 +10579,7 @@ bool Scene::LoadVisibleFacesData(std::map<std::string, std::unordered_set<int>>&
std::unordered_set<int>& face_visible_relative,
std::map<std::string, std::unordered_set<int>>& edge_faces_map,
std::map<std::string, std::unordered_set<int>>& delete_edge_faces_map,
std::map<std::string, std::unordered_set<int>>& face_normal_visible_map,
std::string& basePath) {
printf("LoadVisibleFacesData %s\n", basePath.c_str());
std::ifstream mapFile(basePath + "_visible_faces_map.txt");
@@ -10403,6 +10648,24 @@ bool Scene::LoadVisibleFacesData(std::map<std::string, std::unordered_set<int>>&
}
mapFile3.close();
std::ifstream mapFile4(basePath + "_face_normal_visible_map.txt");
if (!mapFile4.is_open()) {
return false;
}
while (std::getline(mapFile4, line)) {
std::istringstream iss(line);
std::string image_name;
iss >> image_name;
std::unordered_set<int> faces;
int face_index;
while (iss >> face_index) {
faces.insert(face_index);
}
face_normal_visible_map[image_name] = faces;
}
mapFile4.close();
return true;
}
@@ -10501,7 +10764,7 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
// printf("basePath=%s\n", basePath.c_str());
if (!LoadVisibleFacesData(visible_faces_map, face_visible_relative, edge_faces_map, delete_edge_faces_map, basePath))
if (!LoadVisibleFacesData(visible_faces_map, face_visible_relative, edge_faces_map, delete_edge_faces_map, face_normal_visible_map, basePath))
{
printf("LoadVisibleFacesData error\n");
}
@@ -10655,6 +10918,20 @@ bool Scene::is_face_delete_edge(const std::string& image_name, int face_index) {
return false;
}
bool Scene::is_face_normal_visible_map(const std::string& image_name, int face_index) {
#ifndef MASK_FACE_OCCLUSION
return true;
#endif
auto it = face_normal_visible_map.find(image_name);
if (it != face_normal_visible_map.end()) {
return it->second.find(face_index) != it->second.end();
}
return false;
}
void Scene::SegmentMeshBasedOnCurvature(Mesh::FaceIdxArr& regionMap, float curvatureThreshold) {
// 确保网格数据有效
if (mesh.faces.empty() || mesh.vertices.empty() ||
+99 -153
View File
@@ -1352,14 +1352,88 @@ class ModelProcessor:
face_visible = v0_visible | v1_visible | v2_visible
# ============ 新增:法线夹角过滤 ============
# 1. 获取面片法线(向量化计算,性能更好)
if not hasattr(self, 'face_normals_tensor'):
# 从mesh获取顶点和面片索引
vertices = np.asarray(self.mesh.vertices) # 形状: (V, 3)
triangles = np.asarray(self.mesh.triangles) # 形状: (F, 3)
# 向量化计算法线
# 获取所有三角形的顶点
v0 = vertices[triangles[:, 0]] # 形状: (F, 3)
v1 = vertices[triangles[:, 1]] # 形状: (F, 3)
v2 = vertices[triangles[:, 2]] # 形状: (F, 3)
# 计算边向量
edge1 = v1 - v0
edge2 = v2 - v0
# 向量化叉积
# np.cross支持批量计算
normals = np.cross(edge1, edge2)
# 计算法线长度
norms = np.linalg.norm(normals, axis=1, keepdims=True)
# 避免除零
norms[norms < 1e-6] = 1.0
# 归一化
normals = normals / norms
# 将退化三角形的法线设为默认值
mask = norms[:, 0] < 1e-6
if np.any(mask):
normals[mask] = np.array([0.0, 0.0, 1.0])
# 转换为GPU张量
self.face_normals_tensor = torch.tensor(normals, device=self.device, dtype=torch.float32)
# 2. 计算摄像机方向(从面片中心指向摄像机)
# 获取面片顶点坐标
v0_pos = self.vertices_tensor[v0_indices]
v1_pos = self.vertices_tensor[v1_indices]
v2_pos = self.vertices_tensor[v2_indices]
# 计算面片中心
face_centers = (v0_pos + v1_pos + v2_pos) / 3.0
# 计算摄像机方向向量
camera_pos = torch.tensor(eye, device=self.device, dtype=torch.float32)
if len(camera_pos.shape) == 1:
camera_pos = camera_pos.unsqueeze(0) # 从(3)变为(1,3)
# 方向:从面片中心指向摄像机
view_dirs = camera_pos - face_centers
view_dirs = view_dirs / torch.norm(view_dirs, dim=1, keepdim=True)
# 3. 计算法线与摄像机方向的夹角
face_normals = self.face_normals_tensor
# 归一化法线
face_normals = face_normals / torch.norm(face_normals, dim=1, keepdim=True)
# 计算点积(余弦值)
cos_angles = torch.sum(face_normals * view_dirs, dim=1)
# 计算角度(弧度转角度)
angles_deg = torch.acos(torch.clamp(cos_angles, -1.0, 1.0)) * 180.0 / torch.pi
# 4. 创建法线可见性掩码(夹角 ≤ 45度)
normal_visible_mask = angles_deg <= 40.0
# 5. 结合遮挡可见性和法线可见性
face_normal_visible = face_visible & normal_visible_mask
# ============ 法线夹角过滤结束 ============
# 使用与CPU版本相同的后续处理
shrunk_visibility = self._shrink_face_visibility(face_visible.cpu().numpy(), 6)
expanded_visibility = self._expand_face_visibility(face_visible.cpu().numpy(), 30)
shrunk_visibility2 = self._shrink_face_visibility(face_visible.cpu().numpy(), 50)
shrunk_visibility = self._shrink_face_visibility(face_visible.cpu().numpy(), 12)
expanded_visibility = self._expand_face_visibility(face_visible.cpu().numpy(), 0)
shrunk_visibility2 = self._shrink_face_visibility(face_visible.cpu().numpy(), 0)
expanded_edge = expanded_visibility & ~shrunk_visibility2
delete_edge = face_visible.cpu().numpy() & ~shrunk_visibility
return shrunk_visibility, expanded_edge, delete_edge
return shrunk_visibility, expanded_edge, delete_edge, face_normal_visible
"""
def _gen_depth_image_gpu(self, cam_data, render):
@@ -1390,6 +1464,7 @@ class ModelProcessor:
visible_faces_dict = {}
edge_faces_dict = {}
delete_edge_faces_dict = {}
face_normal_visible_dict = {}
total_start = time.time()
@@ -1415,13 +1490,14 @@ class ModelProcessor:
# continue
start_time = time.time()
face_visibility, face_edge, face_delete_edge = self._flag_model_gpu(camera_data)
face_visibility, face_edge, face_delete_edge, face_normal_visible = self._flag_model_gpu(camera_data)
processing_time = time.time() - start_time
visible_faces = np.where(face_visibility)[0].tolist()
visible_faces_dict[img_name] = visible_faces
edge_faces_dict[img_name] = np.where(face_edge)[0].tolist()
delete_edge_faces_dict[img_name] = np.where(face_delete_edge)[0].tolist()
face_normal_visible_dict[img_name] = np.where(face_normal_visible.cpu().numpy())[0].tolist()
print(f"图像 {img_name} 处理完成,耗时: {processing_time:.2f}秒,可见面数量{len(visible_faces)}")
@@ -1429,12 +1505,13 @@ class ModelProcessor:
print(f"所有图像处理完成,总耗时: {total_time:.2f}秒")
print(f"平均每张图像耗时: {total_time/len(images):.2f}秒")
self.save_occlusion_data(visible_faces_dict, edge_faces_dict, delete_edge_faces_dict, self.asset_dir)
self.save_occlusion_data(visible_faces_dict, edge_faces_dict, delete_edge_faces_dict, face_normal_visible_dict, self.asset_dir)
return {
"result1": visible_faces_dict,
"result2": edge_faces_dict,
"result3": delete_edge_faces_dict
"result3": delete_edge_faces_dict,
"result4": face_normal_visible_dict
}
#"""
@@ -1928,64 +2005,6 @@ class ModelProcessor:
depth = render.render_to_depth_image(z_in_view_space=True)
return np.asarray(depth)
def sort_vertices(vertices_original):
return sorted(
(v for v in vertices_original),
key=lambda v: (v.co.x, v.co.y, v.co.z)
)
def _flag_model(self, camera_data, face_points):
"""标记可见顶点"""
vertex_visible = []
vertex_occlusion = []
depth_images = []
render = o3d.visualization.rendering.OffscreenRenderer(camera_data['width'], camera_data['height'])
material = o3d.visualization.rendering.MaterialRecord()
render.scene.add_geometry("mesh", self.mesh, material)
# 生成深度图
depth_image = self._gen_depth_image(camera_data, render)
# 计算可见性
R = self.qvec2rotmat(camera_data['qvec']).T
eye = -R @ camera_data['tvec']
# eye = camera_data['tvec']
# final_visible_list, final_occlusion_list, final_vertex_difference_list = self._compute_vertex_in_frustum(
final_visible_list, final_occlusion_list = self._compute_vertex_in_frustum(
camera_data['fx'], camera_data['fy'],
camera_data['cx'], camera_data['cy'],
R, eye,
camera_data['height'], camera_data['width'],
depth_image,camera_data['qvec'], camera_data['tvec']
)
print("_flag_model", len(final_occlusion_list), len(self.mesh.vertices), len(self.mesh.vertex_colors))
# 获取三角形面片数组
triangles = np.asarray(self.mesh.triangles)
face_visible_bitmap = np.zeros(len(triangles), dtype=bool)
# 遍历所有面片
for face_idx, face in enumerate(triangles):
v0, v1, v2 = face
face_visible_bitmap[face_idx] = any([ # any all
final_visible_list[v0],
final_visible_list[v1],
final_visible_list[v2]
])
shrunk_visibility = self._shrink_face_visibility(face_visible_bitmap, 6) # 6 10
expanded_visibility = self._expand_face_visibility(face_visible_bitmap, 30)
shrunk_visibility2 = self._shrink_face_visibility(face_visible_bitmap, 50)
expanded_edge = expanded_visibility & ~shrunk_visibility2
delete_edge = face_visible_bitmap & ~shrunk_visibility
return shrunk_visibility, expanded_edge, delete_edge
def _flag_contour(self, camera_data, face_points):
"""标记可见顶点"""
vertex_visible = []
@@ -2075,97 +2094,10 @@ class ModelProcessor:
face_visible_bitmap = np.ones(len(triangles), dtype=bool) # 临时填充
return face_visible_bitmap, face_edge
"""
def _mask_face_occlusion(self):
# 读取相机数据
cameras = read_cameras_text(os.path.join(self.pose_path, "cameras.txt"))
images = read_images_text(os.path.join(self.pose_path, "images.txt"))
# cameras = read_cameras_text(os.path.join(self.pose_path, "backup_cameras.txt"))
# images = read_images_text(os.path.join(self.pose_path, "backup_images.txt"))
face_points_sorted_path = os.path.join(self.pose_path, "face_points_sorted.txt")
print("face_points_sorted_path=", face_points_sorted_path)
#face_points = read_int_text(face_points_sorted_path)
face_points = read_indices_from_file(face_points_sorted_path)
# face_points = {}
camera_data = {}
for img in images.values():
if self.mask_image == img.name[:-4]:
camera = cameras[img.camera_id]
camera_data = {
"qvec": img.qvec,
"tvec": img.tvec,
"fx": camera.params[0],
"fy": camera.params[1],
"cx": camera.params[2],
"cy": camera.params[3],
"width": camera.width,
"height": camera.height,
"name": img.name[:-4]
}
# print(face_points)
self._flag_model(camera_data, face_points)
"""
def _mask_occlusion(self):
# 读取相机数据
cameras = read_cameras_text(os.path.join(self.pose_path, "cameras.txt"))
images = read_images_text(os.path.join(self.pose_path, "images.txt"))
camera_data = {}
countour_faces_dict = {}
visible_faces_dict = {}
edge_faces_dict = {}
delete_edge_faces_dict = {}
total_start = time.time()
n = 0
for img in images.values():
camera = cameras[img.camera_id]
camera_data = {
"qvec": img.qvec,
"tvec": img.tvec,
"fx": camera.params[0],
"fy": camera.params[1],
"cx": camera.params[2],
"cy": camera.params[3],
"width": camera.width,
"height": camera.height,
"name": img.name[:-4]
}
img_name = img.name[:-4]
# if (img_name!="73_8" and img_name!="52_8" and img_name!="62_8"):
# if (img_name!="52_8" and img_name!="62_8"):
# if (img_name!="52_8"):
# continue
start_time = time.time()
face_visibility, face_edge, face_delete_edge = self._flag_model(camera_data, None)
processing_time = time.time() - start_time
visible_faces = np.where(face_visibility)[0].tolist()
visible_faces_dict[img.name[:-4]] = visible_faces
edge_faces_dict[img.name[:-4]] = np.where(face_edge)[0].tolist()
delete_edge_faces_dict[img.name[:-4]] = np.where(face_delete_edge)[0].tolist()
n += 1
print(f"图像={img_name},耗时={processing_time:.2f}秒,可见面数={len(visible_faces)}")
total_time = time.time() - total_start
print(f"所有图像处理完成,总耗时: {total_time:.2f}秒")
print(f"平均每张图像耗时: {total_time/len(images):.2f}秒")
self.save_occlusion_data(visible_faces_dict, edge_faces_dict, delete_edge_faces_dict, self.asset_dir)
return {"result1": visible_faces_dict, "result2": edge_faces_dict, "result3": delete_edge_faces_dict}
def save_occlusion_data(self, result1: Dict[str, List[int]],
result2: Dict[str, List[int]],
result3: Dict[str, List[int]],
result4: Dict[str, List[int]],
base_path: str) -> None:
"""
保存遮挡数据到文件
@@ -2201,6 +2133,9 @@ class ModelProcessor:
for image_name, face_list in result3.items():
delete_edge_faces_map[image_name] = set(face_list)
face_normal_visible_map: Dict[str, Set[int]] = {}
for image_name, face_list in result4.items():
face_normal_visible_map[image_name] = set(face_list)
# 保存 visible_faces_map
try:
@@ -2246,6 +2181,17 @@ class ModelProcessor:
except IOError as e:
print(f"Error writing delete_edge_faces_map file: {e}")
# 保存 face_normal_visible_map
try:
file_name = "_face_normal_visible_map.txt"
file_path = Path(base_path) / file_name
with open(file_path, "w", encoding='utf-8') as map_file4:
for image_name, face_set in face_normal_visible_map.items():
line = image_name + " " + " ".join(str(face) for face in face_set) + "\n"
map_file4.write(line)
except IOError as e:
print(f"Error writing delete_edge_faces_map file: {e}")
def process(self):
print("process")