Browse Source

颜色溢出优化

master
hesuicong 1 month ago
parent
commit
e18f91e894
  1. 3
      libs/MVS/Scene.h
  2. 555
      libs/MVS/SceneTexture.cpp
  3. 100
      libs/MVS/mask_face_occlusion.py

3
libs/MVS/Scene.h

@ -65,6 +65,7 @@ public: @@ -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: @@ -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: @@ -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);

555
libs/MVS/SceneTexture.cpp

@ -461,6 +461,7 @@ public: @@ -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;
@ -801,6 +802,8 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr @@ -801,6 +802,8 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
}
std::vector<int> faceViewCount(faces.size(), 0);
std::vector<std::vector<Color>> faceViewColors(faces.size()); // 存储每个面片在各个视图下的颜色
std::vector<std::vector<float>> faceViewQualities(faces.size()); // 存储每个面片在各个视图下的质量
// extract array of faces viewed by each image
IIndexArr views(_views);
@ -819,8 +822,8 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr @@ -819,8 +822,8 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
#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;
@ -929,160 +932,7 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr @@ -929,160 +932,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);
@ -1115,7 +965,11 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr @@ -1115,7 +965,11 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
skipVisibilityCheck = true;
}
if (scene.is_face_visible(strName.c_str(), idxFace))
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);
// if (is_face_visible && is_face_normal_visible_map)
if (is_face_visible)
// if (skipVisibilityCheck || scene.is_face_visible(strName.c_str(), idxFace))
{
rasterer.Project(facet, triangleRasterizer);
@ -1250,6 +1104,13 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr @@ -1250,6 +1104,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
@ -1262,8 +1123,15 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr @@ -1262,8 +1123,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) {
@ -1280,224 +1148,90 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr @@ -1280,224 +1148,90 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
// Temp
/*
for (int_t idx=0; idx<(int_t)views.size(); ++idx)
{
#pragma omp flush (bAbort)
if (bAbort)
{
++progress;
// 第一步:计算每个面片的最终颜色(基于最接近平均颜色的3个视图)
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
Colors faceFinalColors; // 存储每个面片的最终颜色
std::vector<float> faceFinalQualities(faces.size(), 0.0f); // 存储每个面片的最终质量
// 使用循环初始化
for (size_t i = 0; i < faces.size(); ++i) {
faceFinalColors.push_back(Color::ZERO);
}
for (FIndex idxFace = 0; idxFace < faces.size(); ++idxFace) {
const std::vector<Color>& colors = faceViewColors[idxFace];
const std::vector<float>& qualities = faceViewQualities[idxFace];
if (colors.empty()) {
continue;
}
const IIndex idxView(views[(IIndex)idx]);
Image& imageData = images[idxView];
if (!imageData.IsValid()) {
++progress;
continue;
// 1. 计算所有视图颜色的平均值
Color avgColor = Color::ZERO;
for (const Color& color : colors) {
avgColor += color;
}
avgColor /= (float)colors.size();
// 2. 计算每个视图颜色与平均值的差异
std::vector<std::pair<float, int>> colorDistances; // (距离, 索引)
colorDistances.reserve(colors.size());
for (size_t i = 0; i < colors.size(); ++i) {
float distance = cv::norm(colors[i] - avgColor);
colorDistances.emplace_back(distance, i);
}
// 3. 按距离排序(从小到大)
std::sort(colorDistances.begin(), colorDistances.end(),
[](const std::pair<float, int>& a, const std::pair<float, int>& b) {
return a.first < b.first;
});
const Image& imageData = images[idxView];
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);
bool is_face_normal_visible_map = scene.is_face_normal_visible_map(strName.c_str(), idxFace);
// 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)) {
#ifdef TEXOPT_USE_OPENMP
bAbort = true;
#pragma omp flush (bAbort)
continue;
#else
return false;
#endif
}
imageData.UpdateCamera(scene.platforms);
// compute gradient magnitude
imageData.image.toGray(imageGradMag, cv::COLOR_BGR2GRAY, true);
cv::Mat grad[2];
mGrad[0].resize(imageGradMag.rows, imageGradMag.cols);
grad[0] = cv::Mat(imageGradMag.rows, imageGradMag.cols, cv::DataType<real>::type, (void*)mGrad[0].data());
mGrad[1].resize(imageGradMag.rows, imageGradMag.cols);
grad[1] = cv::Mat(imageGradMag.rows, imageGradMag.cols, cv::DataType<real>::type, (void*)mGrad[1].data());
cv::Sobel(imageGradMag, grad[0], cv::DataType<real>::type, 1, 0, 3, 1.0/8.0);
cv::Sobel(imageGradMag, grad[1], cv::DataType<real>::type, 0, 1, 3, 1.0/8.0);
// 4. 只保留最接近的3个视图
size_t numToKeep = colorDistances.size(); // std::min((size_t)3, colorDistances.size());
Color finalColor = Color::ZERO;
float finalQuality = 0.0f;
(TImage<real>::EMatMap)imageGradMag = (mGrad[0].cwiseAbs2()+mGrad[1].cwiseAbs2()).cwiseSqrt();
// apply some blur on the gradient to lower noise/glossiness effects onto face-quality score
cv::GaussianBlur(imageGradMag, imageGradMag, cv::Size(15, 15), 0, 0, cv::BORDER_DEFAULT);
// 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());
std::unordered_set<FIndex> tempFaces;
{
// RasterMesh rasterer(vertices, imageData.camera, depthMap, faceMap);
RasterMesh rasterer(*this, vertices, imageData.camera, depthMap, faceMap, scene.mesh, false);
RasterMesh::Triangle triangle;
RasterMesh::TriangleRasterizer triangleRasterizer(triangle, rasterer);
if (nIgnoreMaskLabel >= 0) {
// import mask
BitMatrix bmask;
// std::cout << "nIgnoreMaskLabel is open" << std::endl;
DepthEstimator::ImportIgnoreMask(imageData, imageData.GetSize(), (uint16_t)OPTDENSE::nIgnoreMaskLabel, bmask, &rasterer.mask);
} else if (nIgnoreMaskLabel == -1) {
// creating mask to discard invalid regions created during image radial undistortion
rasterer.mask = DetectInvalidImageRegions(imageData.image);
#if TD_VERBOSE != TD_VERBOSE_OFF
if (VERBOSITY_LEVEL > 2)
cv::imwrite(String::FormatString("umask%04d.png", idxView), rasterer.mask);
#endif
}
rasterer.Clear();
for (FIndex idxFace : cameraFaces) {
rasterer.validFace = true;
const Face& facet = faces[idxFace];
rasterer.idxFace = idxFace;
bool bExistFace = facesDatas[idxFace].empty();
if (scene.is_face_visible(strName.c_str(), idxFace) || bExistFace)
{
rasterer.Project(facet, triangleRasterizer);
if (!rasterer.validFace)
rasterer.Project(facet, triangleRasterizer);
}
}
tempFaces = PerformLocalDepthConsistencyCheck(depthMap, faceMap, scene.mesh, idxView, strName);
for (size_t i = 0; i < numToKeep; ++i) {
int viewIdx = colorDistances[i].second;
finalColor += colors[viewIdx];
finalQuality += qualities[viewIdx];
}
// compute the projection area of visible faces
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
CLISTDEF0IDX(uint32_t,FIndex) areas(faces.size());
areas.Memset(0);
#endif
#ifdef TEXOPT_USE_OPENMP
#pragma omp critical
#endif
{
for (int j=0; j<faceMap.rows; ++j) {
for (int i=0; i<faceMap.cols; ++i) {
const FIndex& idxFace = faceMap(j,i);
// ASSERT((idxFace == NO_ID && depthMap(j,i) == 0) || (idxFace != NO_ID && depthMap(j,i) > 0));
if (idxFace == NO_ID)
continue;
FaceDataArr& faceDatas = facesDatas[idxFace];
const Pixel8U& pixel = imageData.image(j, i);
// 假设是8位图像,RGB三个通道任一超过250即视为过曝
if (pixel.r > 250 || pixel.g > 250 || pixel.b > 250) {
// continue;
}
float brightnessPerceptual = (0.299f * pixel.r + 0.587f * pixel.g + 0.114f * pixel.b) / 255.0f;
// printf("brightnessPerceptual=%f\n", brightnessPerceptual);
// 5. 计算最终颜色和质量
finalColor /= (float)numToKeep;
finalQuality /= (float)numToKeep;
if (brightnessPerceptual>0.95f)
continue;
faceFinalColors[idxFace] = finalColor;
faceFinalQualities[idxFace] = finalQuality;
{
if (depthMap(j,i)>999.0f)
{
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
uint32_t& area = areas[idxFace];
if (area++ == 0) {
#else
if (faceDatas.empty() || faceDatas.back().idxView != idxView) {
#endif
// create new face-data
FaceData& faceData = faceDatas.emplace_back();
faceData.idxView = idxView;
faceData.quality = imageGradMag(j,i);
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
faceData.color = imageData.image(j,i);
#endif
faceData.bInvalidFacesRelative = true;
} else {
// update face-data
ASSERT(!faceDatas.empty());
FaceData& faceData = faceDatas.back();
ASSERT(faceData.idxView == idxView);
faceData.quality = imageGradMag(j,i);
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
faceData.color = Color(imageData.image(j,i));
#endif
faceData.bInvalidFacesRelative = true;
}
continue;
}
}
uint32_t& area = areas[idxFace];
if (area++ == 0) {
// create new face-data
FaceData& faceData = faceDatas.emplace_back();
faceData.idxView = idxView;
faceData.quality = imageGradMag(j,i);
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
faceData.color = imageData.image(j,i);
#endif
if (depthMap(j,i)>999.0f)
faceData.bInvalidFacesRelative = true;
} else {
// update face-data
ASSERT(!faceDatas.empty());
FaceData& faceData = faceDatas.back();
ASSERT(faceData.idxView == idxView);
faceData.quality += imageGradMag(j,i);
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
faceData.color += Color(imageData.image(j,i));
#endif
if (depthMap(j,i)>999.0f)
faceData.bInvalidFacesRelative = true;
}
}
}
// adjust face quality with camera angle relative to face normal
// tries to increase chances of a camera with perpendicular view on the surface (smoothened normals) to be selected
FOREACH(idxFace, facesDatas) {
FaceDataArr& faceDatas = facesDatas[idxFace];
if (faceDatas.empty() || faceDatas.back().idxView != idxView)
continue;
const Face& f = faces[idxFace];
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())));
faceDatas.back().quality *= SQUARE(cosFaceCam);
}
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
FOREACH(idxFace, areas) {
const uint32_t& area = areas[idxFace];
if (area > 0) {
Color& color = facesDatas[idxFace].back().color;
color = RGB2YCBCR(Color(color * (1.f/(float)area)));
}
// 第二步:更新facesDatas,只保留最终颜色和质量
FaceDataArr& faceDatas = facesDatas[idxFace];
if (!faceDatas.empty()) {
// 只保留第一个FaceData,并用最终颜色和质量更新它
faceDatas.resize(1);
FaceData& faceData = faceDatas.back();
faceData.quality = finalQuality;
faceData.color = finalColor;
// 注意:这里不再保留原始的idxView,因为颜色是多个视图的混合
// 您可能需要根据需要调整idxView的值
}
#endif
}
++progress;
}
// */
#endif
//*/
// Temp
#ifdef TEXOPT_USE_OPENMP
@ -3300,7 +3034,8 @@ bool MeshTexture::CreateVirtualFaces6(FaceDataViewArr& facesDatas, FaceDataViewA @@ -3300,7 +3034,8 @@ 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); // 亮度评分函数
@ -3323,7 +3058,9 @@ bool MeshTexture::CreateVirtualFaces6(FaceDataViewArr& facesDatas, FaceDataViewA @@ -3323,7 +3058,9 @@ 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); // 亮度评分函数
@ -3757,8 +3494,12 @@ bool MeshTexture::CreateVirtualFaces6(FaceDataViewArr& facesDatas, FaceDataViewA @@ -3757,8 +3494,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 {
// 使用过滤后的视图重新计算平均值
@ -3775,6 +3516,9 @@ bool MeshTexture::CreateVirtualFaces6(FaceDataViewArr& facesDatas, FaceDataViewA @@ -3775,6 +3516,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)
@ -4450,6 +4194,35 @@ MeshTexture::FaceDataArr MeshTexture::MergeFaceData(const FaceDataArr& data1, co @@ -4450,6 +4194,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
{
@ -4647,7 +4420,10 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView @@ -4647,7 +4420,10 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
}
else
{
filteredCams.push_back(idxView);
// if (angleDeg <= 30.0f)
{
filteredCams.push_back(idxView);
}
/*
float brightnessScore = CalculateBrightnessScore(imageData); // 亮度评分函数
@ -4746,6 +4522,10 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView @@ -4746,6 +4522,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);
@ -4818,7 +4598,9 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView @@ -4818,7 +4598,9 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
if (colorDistance <= maxColorDeviation &&
luminanceDistance <= maxLuminanceDeviation)
{
validIndices.push_back(n);
if (scene.is_face_normal_visible_map(strName, virtualFaceCenterFaceID))
validIndices.push_back(n);
}
else
{
@ -4842,21 +4624,25 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView @@ -4842,21 +4624,25 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
if (cosFaceToCenter<dynamicCosTh)
{
if (nInvalidViewCount<=2)
{
validIndices.push_back(n);
}
else
{
// if ((colorDistance <= 350.0f))
validIndices.push_back(n);
validIndices.push_back(n);
}
}
else
{
if (nInvalidViewCount<=2)
{
validIndices.push_back(n);
}
else
{
// if (bColorSimilarity)
validIndices.push_back(n);
validIndices.push_back(n);
}
}
@ -4942,6 +4728,11 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView @@ -4942,6 +4728,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 {
// 使用过滤后的视图重新计算平均值
@ -4953,6 +4744,11 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView @@ -4953,6 +4744,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
}
}
@ -7630,12 +7426,16 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT @@ -7630,12 +7426,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()) {
labels[l] = virtualLabels[virtualIdx];
// 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)",
@ -7646,6 +7446,8 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT @@ -7646,6 +7446,8 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT
DEBUG_EXTRA("Warning: Face index out of bounds: %u (max: %u)",
l, mapFaceToVirtualFace.size()-1);
}
// ++n;
}
//*/
// 修改后安全版本
@ -10600,6 +10402,7 @@ bool Scene::LoadVisibleFacesData(std::map<std::string, std::unordered_set<int>>& @@ -10600,6 +10402,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");
@ -10668,6 +10471,24 @@ bool Scene::LoadVisibleFacesData(std::map<std::string, std::unordered_set<int>>& @@ -10668,6 +10471,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;
}
@ -10766,7 +10587,7 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi @@ -10766,7 +10587,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");
}
@ -10920,6 +10741,20 @@ bool Scene::is_face_delete_edge(const std::string& image_name, int face_index) { @@ -10920,6 +10741,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() ||

100
libs/MVS/mask_face_occlusion.py

@ -1352,6 +1352,80 @@ class ModelProcessor: @@ -1352,6 +1352,80 @@ 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 <= 70.0
# 5. 结合遮挡可见性和法线可见性
face_normal_visible = face_visible & normal_visible_mask
# ============ 法线夹角过滤结束 ============
# 使用与CPU版本相同的后续处理
shrunk_visibility = self._shrink_face_visibility(face_visible.cpu().numpy(), 9)
expanded_visibility = self._expand_face_visibility(face_visible.cpu().numpy(), 30)
@ -1359,7 +1433,7 @@ class ModelProcessor: @@ -1359,7 +1433,7 @@ class ModelProcessor:
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: @@ -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: @@ -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: @@ -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
}
#"""
@ -2020,6 +2097,7 @@ class ModelProcessor: @@ -2020,6 +2097,7 @@ class ModelProcessor:
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:
"""
保存遮挡数据到文件
@ -2055,6 +2133,9 @@ class ModelProcessor: @@ -2055,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:
@ -2100,6 +2181,17 @@ class ModelProcessor: @@ -2100,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")

Loading…
Cancel
Save