Compare commits
25
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03929e5b95 | ||
|
|
1086eb09c5 | ||
|
|
7d0487ea30 | ||
|
|
cbe065a6a1 | ||
|
|
488f5a3f93 | ||
|
|
e18f91e894 | ||
|
|
eafae796c2 | ||
|
|
35ac5deff9 | ||
|
|
f9e7f3f18b | ||
|
|
d035a20e91 | ||
|
|
d11d956b58 | ||
|
|
743835d1b0 | ||
|
|
4968fcc954 | ||
|
|
dbe42abf1f | ||
|
|
5685a46331 | ||
|
|
bbddc87ccc | ||
|
|
634590e6d4 | ||
|
|
955419e110 | ||
|
|
1483610ed5 | ||
|
|
6bd6b27f17 | ||
|
|
37ebcdf0fd | ||
|
|
ef09d2521a | ||
|
|
60f024abaf | ||
|
|
469f3df88d | ||
|
|
bc461bd595 |
No files matched your search
@@ -1098,9 +1098,7 @@ int main(int argc, LPCTSTR* argv)
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
printf("Now\n");
|
printf("Now\n");
|
||||||
VERBOSE(MAKE_PATH_SAFE(OPT::strMeshFileName));
|
VERBOSE(MAKE_PATH_SAFE(OPT::strMeshFileName));
|
||||||
bool bVertexColorExists = false;
|
if (!OPT::strMeshFileName.empty() && !scene.mesh.Load(MAKE_PATH_SAFE(OPT::strMeshFileName))) {
|
||||||
bool *pBExistVertexColor = &bVertexColorExists;
|
|
||||||
if (!OPT::strMeshFileName.empty() && !scene.mesh.Load(MAKE_PATH_SAFE(OPT::strMeshFileName), pBExistVertexColor)) {
|
|
||||||
VERBOSE("error: cannot load mesh file");
|
VERBOSE("error: cannot load mesh file");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -1136,7 +1134,7 @@ int main(int argc, LPCTSTR* argv)
|
|||||||
if (!scene.TextureMesh(OPT::nResolutionLevel, OPT::nMinResolution, OPT::minCommonCameras, OPT::fOutlierThreshold, OPT::fRatioDataSmoothness,
|
if (!scene.TextureMesh(OPT::nResolutionLevel, OPT::nMinResolution, OPT::minCommonCameras, OPT::fOutlierThreshold, OPT::fRatioDataSmoothness,
|
||||||
OPT::bGlobalSeamLeveling, OPT::bLocalSeamLeveling, OPT::nTextureSizeMultiple, OPT::nRectPackingHeuristic, Pixel8U(OPT::nColEmpty),
|
OPT::bGlobalSeamLeveling, OPT::bLocalSeamLeveling, OPT::nTextureSizeMultiple, OPT::nRectPackingHeuristic, Pixel8U(OPT::nColEmpty),
|
||||||
OPT::fSharpnessWeight, OPT::nIgnoreMaskLabel, OPT::nMaxTextureSize, views, baseFileName, OPT::bOriginFaceview,
|
OPT::fSharpnessWeight, OPT::nIgnoreMaskLabel, OPT::nMaxTextureSize, views, baseFileName, OPT::bOriginFaceview,
|
||||||
OPT::strInputFileName, OPT::strMeshFileName, *pBExistVertexColor))
|
OPT::strInputFileName, OPT::strMeshFileName))
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
VERBOSE("Mesh texturing completed: %u vertices, %u faces (%s)", scene.mesh.vertices.GetSize(), scene.mesh.faces.GetSize(), TD_TIMER_GET_FMT().c_str());
|
VERBOSE("Mesh texturing completed: %u vertices, %u faces (%s)", scene.mesh.vertices.GetSize(), scene.mesh.faces.GetSize(), TD_TIMER_GET_FMT().c_str());
|
||||||
|
|
||||||
|
|||||||
@@ -579,16 +579,6 @@ inline TPoint3<TYPE> ComputeTriangleNormal(const TPoint3<TYPE>& v0, const TPoint
|
|||||||
} // ComputeTriangleNormal
|
} // ComputeTriangleNormal
|
||||||
/*----------------------------------------------------------------*/
|
/*----------------------------------------------------------------*/
|
||||||
|
|
||||||
template<typename ColorType>
|
|
||||||
ColorType ComputeTriangleColor(const ColorType& c0, const ColorType& c1, const ColorType& c2) {
|
|
||||||
// 手动计算每个通道的平均值
|
|
||||||
ColorType result;
|
|
||||||
result.r = static_cast<uint8_t>((static_cast<int>(c0.r) + c1.r + c2.r) / 3 + 0.5f); // 整数运算避免溢出
|
|
||||||
result.g = static_cast<uint8_t>((c0.g + c1.g + c2.g) / 3 + 0.5f);
|
|
||||||
result.b = static_cast<uint8_t>((c0.b + c1.b + c2.b) / 3 + 0.5f);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// compute the area of a triangle using Heron's formula
|
// compute the area of a triangle using Heron's formula
|
||||||
template <typename TYPE>
|
template <typename TYPE>
|
||||||
TYPE ComputeTriangleAreaSqLen(TYPE lena, TYPE lenb, TYPE lenc) {
|
TYPE ComputeTriangleAreaSqLen(TYPE lena, TYPE lenb, TYPE lenc) {
|
||||||
|
|||||||
@@ -1,767 +0,0 @@
|
|||||||
#include "ColorComparisonFace.h"
|
|
||||||
#include "Mesh.h" // 在cpp中包含完整定义
|
|
||||||
#include <opencv2/opencv.hpp>
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
using namespace MVS;
|
|
||||||
|
|
||||||
// 辅助函数:检查目录是否存在
|
|
||||||
static bool directoryExists(const std::string& path) {
|
|
||||||
struct stat info;
|
|
||||||
if (stat(path.c_str(), &info) != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (info.st_mode & S_IFDIR) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 辅助函数:创建目录
|
|
||||||
static bool createDirectory(const std::string& path) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
return _mkdir(path.c_str()) == 0;
|
|
||||||
#else
|
|
||||||
return mkdir(path.c_str(), 0755) == 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// 辅助函数:递归创建目录
|
|
||||||
static bool createDirectories(const std::string& path) {
|
|
||||||
size_t pos = 0;
|
|
||||||
std::string dir;
|
|
||||||
|
|
||||||
while ((pos = path.find_first_of("/\\", pos + 1)) != std::string::npos) {
|
|
||||||
dir = path.substr(0, pos);
|
|
||||||
if (!directoryExists(dir)) {
|
|
||||||
if (!createDirectory(dir)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建最终目录
|
|
||||||
if (!directoryExists(path)) {
|
|
||||||
return createDirectory(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构造函数
|
|
||||||
ColorComparisonFace::ColorComparisonFace(const std::string& dir) : outputDir(dir) {
|
|
||||||
// 确保输出目录以斜杠结尾
|
|
||||||
if (!outputDir.empty() && outputDir.back() != '/' && outputDir.back() != '\\') {
|
|
||||||
outputDir += '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建输出目录
|
|
||||||
if (!createDirectories(outputDir)) {
|
|
||||||
printf("⚠️ 无法创建目录: %s\n", outputDir.c_str());
|
|
||||||
} else {
|
|
||||||
printf("✅ 输出目录: %s\n", outputDir.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ColorComparisonFace::addExactTriangleInfo(int faceId,
|
|
||||||
MeshColor gaussianColor,
|
|
||||||
MeshColor originalColor,
|
|
||||||
const cv::Mat& triangleRegionWithAlpha, // 带透明通道
|
|
||||||
const cv::Mat& visualization, // 可视化图像
|
|
||||||
float colorDistance,
|
|
||||||
float threshold,
|
|
||||||
const std::string& filename) {
|
|
||||||
// 存储信息
|
|
||||||
FaceColorInfo info;
|
|
||||||
info.faceId = faceId;
|
|
||||||
info.gaussianColor = gaussianColor;
|
|
||||||
info.originalColor = originalColor;
|
|
||||||
info.triangleRegion = triangleRegionWithAlpha.clone(); // 带透明通道
|
|
||||||
info.visualization = visualization.clone(); // 可视化图像
|
|
||||||
info.colorDistance = colorDistance;
|
|
||||||
info.threshold = threshold;
|
|
||||||
info.filename = filename;
|
|
||||||
|
|
||||||
faceViewColorMap[faceId][filename].push_back(info);
|
|
||||||
printf("addExactTriangleInfo faceId=%d\n", faceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取总face数
|
|
||||||
int ColorComparisonFace::getTotalFaces() const {
|
|
||||||
return faceViewColorMap.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取总记录数
|
|
||||||
int ColorComparisonFace::getTotalRecords() const {
|
|
||||||
int total = 0;
|
|
||||||
for (const auto& faceEntry : faceViewColorMap) {
|
|
||||||
for (const auto& viewEntry : faceEntry.second) {
|
|
||||||
total += viewEntry.second.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取faceid列表
|
|
||||||
std::vector<int> ColorComparisonFace::getFaceIds() const {
|
|
||||||
std::vector<int> faceIds;
|
|
||||||
for (const auto& faceEntry : faceViewColorMap) {
|
|
||||||
faceIds.push_back(faceEntry.first);
|
|
||||||
}
|
|
||||||
return faceIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建三角形区域对比图
|
|
||||||
void ColorComparisonFace::createBatchComparison(int maxBlocksPerRow, int maxFacesPerImage) {
|
|
||||||
if (faceViewColorMap.empty()) {
|
|
||||||
printf("⚠️ 没有颜色信息可生成\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("正在创建三角形区域对比图...\n");
|
|
||||||
printf("总face数: %zu\n", faceViewColorMap.size());
|
|
||||||
printf("总记录数: %d\n", getTotalRecords());
|
|
||||||
|
|
||||||
// 块参数
|
|
||||||
int triangleSize = 200; // 三角形区域显示大小
|
|
||||||
int colorBlockSize = 100; // 颜色块大小
|
|
||||||
int blockMargin = 20; // 块之间的边距
|
|
||||||
int infoHeight = 100; // 信息区域高度
|
|
||||||
|
|
||||||
// 限制处理的face数
|
|
||||||
int facesToProcess = std::min((int)faceViewColorMap.size(), maxFacesPerImage);
|
|
||||||
|
|
||||||
// 处理前N个face
|
|
||||||
int faceCount = 0;
|
|
||||||
for (const auto& faceEntry : faceViewColorMap) {
|
|
||||||
if (faceCount >= facesToProcess) break;
|
|
||||||
|
|
||||||
int faceId = faceEntry.first;
|
|
||||||
const auto& viewMap = faceEntry.second;
|
|
||||||
|
|
||||||
printf("处理 face %d (%zu 个视图)...\n", faceId, viewMap.size());
|
|
||||||
|
|
||||||
// 计算需要的行数和列数
|
|
||||||
int numBlocks = 0;
|
|
||||||
for (const auto& viewEntry : viewMap) {
|
|
||||||
numBlocks += viewEntry.second.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
int numCols = std::min(maxBlocksPerRow, numBlocks);
|
|
||||||
int numRows = (numBlocks + numCols - 1) / numCols;
|
|
||||||
|
|
||||||
// 计算每个块的宽度和高度
|
|
||||||
int blockWidth = colorBlockSize + triangleSize + blockMargin * 3;
|
|
||||||
int blockHeight = triangleSize + blockMargin + infoHeight;
|
|
||||||
|
|
||||||
// 图片总尺寸
|
|
||||||
int totalWidth = numCols * (blockWidth + blockMargin) + blockMargin;
|
|
||||||
int totalHeight = 60 + (numRows * (blockHeight + blockMargin)) + blockMargin;
|
|
||||||
|
|
||||||
// 创建大图
|
|
||||||
cv::Mat faceImage(totalHeight, totalWidth, CV_8UC3, cv::Scalar(245, 245, 245));
|
|
||||||
|
|
||||||
// 添加标题
|
|
||||||
std::string title = cv::format("Face %d - Triangle Region Comparison", faceId);
|
|
||||||
cv::putText(faceImage, title,
|
|
||||||
cv::Point(20, 30),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(0, 0, 0), 2);
|
|
||||||
|
|
||||||
// 添加统计信息
|
|
||||||
int totalRecords = 0;
|
|
||||||
int needFixCount = 0;
|
|
||||||
for (const auto& viewEntry : viewMap) {
|
|
||||||
totalRecords += viewEntry.second.size();
|
|
||||||
for (const auto& info : viewEntry.second) {
|
|
||||||
if (info.colorDistance > info.threshold) needFixCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string stats = cv::format("Views: %zu, Records: %d, Need Fix: %d",
|
|
||||||
viewMap.size(), totalRecords, needFixCount);
|
|
||||||
cv::putText(faceImage, stats,
|
|
||||||
cv::Point(20, 55),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
int blockIndex = 0;
|
|
||||||
int currentRow = 0;
|
|
||||||
int currentCol = 0;
|
|
||||||
|
|
||||||
// 遍历这个face的所有视图
|
|
||||||
for (const auto& viewEntry : viewMap) {
|
|
||||||
const std::string& viewName = viewEntry.first;
|
|
||||||
const std::vector<FaceColorInfo>& infos = viewEntry.second;
|
|
||||||
|
|
||||||
for (const auto& info : infos) {
|
|
||||||
// 计算当前块的位置
|
|
||||||
int blockX = blockMargin + currentCol * (blockWidth + blockMargin);
|
|
||||||
int blockY = 60 + blockMargin + currentRow * (blockHeight + blockMargin);
|
|
||||||
|
|
||||||
// 绘制块背景
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Rect(blockX, blockY, blockWidth, blockHeight),
|
|
||||||
cv::Scalar(255, 255, 255), -1);
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Rect(blockX, blockY, blockWidth, blockHeight),
|
|
||||||
cv::Scalar(200, 200, 200), 2);
|
|
||||||
|
|
||||||
// 绘制高斯颜色块
|
|
||||||
int gaussianX = blockX + blockMargin;
|
|
||||||
int gaussianY = blockY + blockMargin;
|
|
||||||
|
|
||||||
cv::Scalar gaussianBGR(info.gaussianColor[2], info.gaussianColor[1], info.gaussianColor[0]);
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Rect(gaussianX, gaussianY, colorBlockSize, colorBlockSize),
|
|
||||||
gaussianBGR, -1);
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Rect(gaussianX, gaussianY, colorBlockSize, colorBlockSize),
|
|
||||||
cv::Scalar(0, 0, 0), 2);
|
|
||||||
|
|
||||||
// 添加高斯标签
|
|
||||||
cv::putText(faceImage, "GAUSSIAN",
|
|
||||||
cv::Point(gaussianX + 10, gaussianY - 5),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
// 绘制三角形区域
|
|
||||||
int triangleX = blockX + blockMargin + colorBlockSize + blockMargin;
|
|
||||||
int triangleY = blockY + blockMargin;
|
|
||||||
|
|
||||||
if (!info.visualization.empty()) {
|
|
||||||
// 调整三角形区域大小
|
|
||||||
cv::Mat resizedTriangle;
|
|
||||||
cv::resize(info.visualization, resizedTriangle, cv::Size(triangleSize, triangleSize));
|
|
||||||
|
|
||||||
// 将三角形区域绘制到指定位置(已经是RGB顺序)
|
|
||||||
resizedTriangle.copyTo(faceImage(cv::Rect(triangleX, triangleY, triangleSize, triangleSize)));
|
|
||||||
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Rect(triangleX, triangleY, triangleSize, triangleSize),
|
|
||||||
cv::Scalar(0, 0, 0), 2);
|
|
||||||
|
|
||||||
// 添加三角形标签
|
|
||||||
cv::putText(faceImage, "TRIANGLE",
|
|
||||||
cv::Point(triangleX + 10, triangleY - 5),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0), 1);
|
|
||||||
} else {
|
|
||||||
// 如果没有三角形区域,绘制占位符
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Rect(triangleX, triangleY, triangleSize, triangleSize),
|
|
||||||
cv::Scalar(200, 200, 200), -1);
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Rect(triangleX, triangleY, triangleSize, triangleSize),
|
|
||||||
cv::Scalar(150, 150, 150), 2);
|
|
||||||
|
|
||||||
cv::putText(faceImage, "NO TRIANGLE",
|
|
||||||
cv::Point(triangleX + triangleSize/2 - 40, triangleY + triangleSize/2),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(100, 100, 100), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 绘制信息区域
|
|
||||||
int infoY = blockY + blockMargin + triangleSize;
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Rect(blockX, infoY, blockWidth, infoHeight),
|
|
||||||
cv::Scalar(240, 240, 240), -1);
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Rect(blockX, infoY, blockWidth, infoHeight),
|
|
||||||
cv::Scalar(200, 200, 200), 1);
|
|
||||||
|
|
||||||
// 添加视图信息
|
|
||||||
std::string displayName = viewName;
|
|
||||||
if (displayName.length() > 20) {
|
|
||||||
displayName = displayName.substr(0, 18) + "...";
|
|
||||||
}
|
|
||||||
|
|
||||||
cv::putText(faceImage, cv::format("View: %s", displayName.c_str()),
|
|
||||||
cv::Point(blockX + 10, infoY + 20),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.4, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
cv::putText(faceImage, cv::format("Face: %d", info.faceId),
|
|
||||||
cv::Point(blockX + 10, infoY + 40),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.4, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
// 添加颜色值
|
|
||||||
cv::putText(faceImage,
|
|
||||||
cv::format("Gauss: (%d,%d,%d)",
|
|
||||||
info.gaussianColor[0], info.gaussianColor[1], info.gaussianColor[2]),
|
|
||||||
cv::Point(blockX + 10, infoY + 60),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.35, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
cv::putText(faceImage,
|
|
||||||
cv::format("Orgin: (%d,%d,%d)",
|
|
||||||
info.originalColor[0], info.originalColor[1], info.originalColor[2]),
|
|
||||||
cv::Point(blockX + 140, infoY + 60),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.35, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
// 添加距离和阈值
|
|
||||||
cv::putText(faceImage,
|
|
||||||
cv::format("Distance: %.4f", info.colorDistance),
|
|
||||||
cv::Point(blockX + 10, infoY + 80),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.35, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
cv::putText(faceImage,
|
|
||||||
cv::format("Threshold: %.4f", info.threshold),
|
|
||||||
cv::Point(blockX + 10, infoY + 95),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.35, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
// 添加判断结果
|
|
||||||
int resultX = blockX + blockWidth - 60;
|
|
||||||
int resultY = infoY + 25;
|
|
||||||
|
|
||||||
std::string resultText = (info.colorDistance > info.threshold) ? "FIX" : "OK";
|
|
||||||
cv::Scalar resultColor = (info.colorDistance > info.threshold) ? cv::Scalar(0, 0, 255) : cv::Scalar(0, 180, 0);
|
|
||||||
|
|
||||||
cv::putText(faceImage, resultText,
|
|
||||||
cv::Point(resultX, resultY),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.5, resultColor, 1);
|
|
||||||
|
|
||||||
// 更新行和列索引
|
|
||||||
blockIndex++;
|
|
||||||
currentCol++;
|
|
||||||
if (currentCol >= numCols) {
|
|
||||||
currentCol = 0;
|
|
||||||
currentRow++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果已经达到最大块数,跳出循环
|
|
||||||
if (blockIndex >= maxBlocksPerRow * numRows) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果已经达到最大块数,跳出循环
|
|
||||||
if (blockIndex >= maxBlocksPerRow * numRows) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存图片
|
|
||||||
std::string outputPath = outputDir + cv::format("face_%d_triangle_comparison.png", faceId);
|
|
||||||
|
|
||||||
if (!cv::imwrite(outputPath, faceImage)) {
|
|
||||||
printf("❌❌ 无法保存face %d的三角形对比图: %s\n", faceId, outputPath.c_str());
|
|
||||||
} else {
|
|
||||||
printf("✅ face %d三角形对比图已保存: %s\n", faceId, outputPath.c_str());
|
|
||||||
printf(" 尺寸: %d x %d 像素, 视图数: %zu, 记录数: %d\n",
|
|
||||||
totalWidth, totalHeight, viewMap.size(), totalRecords);
|
|
||||||
}
|
|
||||||
|
|
||||||
faceCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存颜色信息到CSV文件
|
|
||||||
saveColorInfoToFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存颜色信息到CSV文件
|
|
||||||
void ColorComparisonFace::saveColorInfoToFile() {
|
|
||||||
// 为每个face保存单独的CSV
|
|
||||||
for (const auto& faceEntry : faceViewColorMap) {
|
|
||||||
int faceId = faceEntry.first;
|
|
||||||
const auto& viewMap = faceEntry.second;
|
|
||||||
|
|
||||||
if (viewMap.empty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string filePath = outputDir + cv::format("face_%d_triangle_colors.csv", faceId);
|
|
||||||
FILE* fp = fopen(filePath.c_str(), "w");
|
|
||||||
|
|
||||||
if (!fp) {
|
|
||||||
printf("❌❌ 无法创建文件: %s\n", filePath.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 写入CSV标题
|
|
||||||
fprintf(fp, "FaceID,View,Gaussian_R,Gaussian_G,Gaussian_B,Distance,Threshold,NeedsFix\n");
|
|
||||||
|
|
||||||
for (const auto& viewEntry : viewMap) {
|
|
||||||
const std::string& viewName = viewEntry.first;
|
|
||||||
const std::vector<FaceColorInfo>& infos = viewEntry.second;
|
|
||||||
|
|
||||||
for (int i = 0; i < (int)infos.size(); i++) {
|
|
||||||
const FaceColorInfo& info = infos[i];
|
|
||||||
bool needsFix = (info.colorDistance > info.threshold);
|
|
||||||
fprintf(fp, "%d,%s,%d,%d,%d,%.4f,%.4f,%s\n",
|
|
||||||
faceId,
|
|
||||||
viewName.c_str(),
|
|
||||||
info.gaussianColor[0], info.gaussianColor[1], info.gaussianColor[2],
|
|
||||||
info.colorDistance, info.threshold,
|
|
||||||
needsFix ? "YES" : "NO");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
printf("✅ face %d三角形颜色信息已保存到: %s\n", faceId, filePath.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存汇总统计信息
|
|
||||||
std::string summaryPath = outputDir + "triangle_comparison_summary.csv";
|
|
||||||
FILE* summaryFp = fopen(summaryPath.c_str(), "w");
|
|
||||||
|
|
||||||
if (summaryFp) {
|
|
||||||
fprintf(summaryFp, "FaceID,Views,TotalRecords,NeedFixRecords,NeedFix%%,MinDistance,MaxDistance,AvgDistance\n");
|
|
||||||
|
|
||||||
for (const auto& faceEntry : faceViewColorMap) {
|
|
||||||
int faceId = faceEntry.first;
|
|
||||||
const auto& viewMap = faceEntry.second;
|
|
||||||
|
|
||||||
int totalRecords = 0;
|
|
||||||
int needFixCount = 0;
|
|
||||||
float minDist = std::numeric_limits<float>::max();
|
|
||||||
float maxDist = 0.0f;
|
|
||||||
float sumDist = 0.0f;
|
|
||||||
|
|
||||||
for (const auto& viewEntry : viewMap) {
|
|
||||||
const std::vector<FaceColorInfo>& infos = viewEntry.second;
|
|
||||||
totalRecords += infos.size();
|
|
||||||
|
|
||||||
for (const auto& info : infos) {
|
|
||||||
if (info.colorDistance > info.threshold) needFixCount++;
|
|
||||||
minDist = std::min(minDist, info.colorDistance);
|
|
||||||
maxDist = std::max(maxDist, info.colorDistance);
|
|
||||||
sumDist += info.colorDistance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float avgDist = (totalRecords > 0) ? (sumDist / totalRecords) : 0.0f;
|
|
||||||
float fixPercentage = (totalRecords > 0) ? (needFixCount * 100.0f / totalRecords) : 0.0f;
|
|
||||||
|
|
||||||
fprintf(summaryFp, "%d,%zu,%d,%d,%.2f%%,%.4f,%.4f,%.4f\n",
|
|
||||||
faceId,
|
|
||||||
viewMap.size(),
|
|
||||||
totalRecords,
|
|
||||||
needFixCount,
|
|
||||||
fixPercentage,
|
|
||||||
minDist,
|
|
||||||
maxDist,
|
|
||||||
avgDist);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(summaryFp);
|
|
||||||
printf("✅ 三角形汇总统计信息已保存到: %s\n", summaryPath.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在ColorComparisonFace.cpp中添加以下函数实现
|
|
||||||
|
|
||||||
// 添加连续区域信息
|
|
||||||
void ColorComparisonFace::addContinuousRegionInfo(int regionId,
|
|
||||||
const std::set<unsigned int>& faceIds, // 修改为 unsigned int
|
|
||||||
MeshColor regionGaussianColor) {
|
|
||||||
if (continuousRegions.find(regionId) == continuousRegions.end()) {
|
|
||||||
ContinuousRegionInfo regionInfo;
|
|
||||||
regionInfo.regionId = regionId;
|
|
||||||
regionInfo.faceIds = faceIds; // 这里类型匹配
|
|
||||||
regionInfo.regionGaussianColor = regionGaussianColor;
|
|
||||||
regionInfo.totalPixels = 0;
|
|
||||||
continuousRegions[regionId] = regionInfo;
|
|
||||||
|
|
||||||
printf("添加连续区域 %d: 包含 %zu 个面\n", regionId, faceIds.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加连续区域在特定视图中的信息
|
|
||||||
void ColorComparisonFace::addRegionViewInfo(int regionId,
|
|
||||||
const std::string& filename,
|
|
||||||
MeshColor viewColor,
|
|
||||||
const cv::Mat& regionImage,
|
|
||||||
const cv::Mat& visualization,
|
|
||||||
float colorDistance) {
|
|
||||||
if (continuousRegions.find(regionId) != continuousRegions.end()) {
|
|
||||||
ContinuousRegionInfo& regionInfo = continuousRegions[regionId];
|
|
||||||
regionInfo.viewColors[filename] = viewColor;
|
|
||||||
regionInfo.viewRegions[filename] = regionImage.clone();
|
|
||||||
regionInfo.viewVisualizations[filename] = visualization.clone();
|
|
||||||
regionInfo.viewDistances[filename] = colorDistance;
|
|
||||||
|
|
||||||
printf(" 视图 %s: 颜色(R=%d,G=%d,B=%d), 距离=%.4f\n",
|
|
||||||
filename.c_str(), viewColor.r, viewColor.g, viewColor.b, colorDistance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取连续区域数
|
|
||||||
int ColorComparisonFace::getTotalRegions() const {
|
|
||||||
return continuousRegions.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建连续区域跨视图比较图
|
|
||||||
void ColorComparisonFace::createContinuousRegionComparison(int maxBlocksPerRow, int maxRegionsPerImage) {
|
|
||||||
if (continuousRegions.empty()) {
|
|
||||||
printf("⚠️ 没有连续区域信息可生成\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("正在创建连续区域跨视图比较图...\n");
|
|
||||||
printf("总连续区域数: %zu\n", continuousRegions.size());
|
|
||||||
|
|
||||||
// 限制处理的区域数
|
|
||||||
int regionsToProcess = std::min((int)continuousRegions.size(), maxRegionsPerImage);
|
|
||||||
|
|
||||||
// 处理前N个区域
|
|
||||||
int regionCount = 0;
|
|
||||||
for (const auto& regionEntry : continuousRegions) {
|
|
||||||
if (regionCount >= regionsToProcess) break;
|
|
||||||
|
|
||||||
int regionId = regionEntry.first;
|
|
||||||
const ContinuousRegionInfo& regionInfo = regionEntry.second;
|
|
||||||
|
|
||||||
printf("处理连续区域 %d: 在 %zu 个视图中可见\n", regionId, regionInfo.viewColors.size());
|
|
||||||
|
|
||||||
if (regionInfo.viewColors.size() < 2) {
|
|
||||||
printf(" 区域 %d 在少于2个视图中可见,跳过跨视图比较\n", regionId);
|
|
||||||
regionCount++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 块参数
|
|
||||||
int regionSize = 200; // 区域显示大小
|
|
||||||
int colorBlockSize = 100; // 颜色块大小
|
|
||||||
int blockMargin = 20; // 块之间的边距
|
|
||||||
int infoHeight = 120; // 信息区域高度
|
|
||||||
|
|
||||||
// 计算需要的行数和列数
|
|
||||||
int numBlocks = regionInfo.viewColors.size();
|
|
||||||
int numCols = std::min(maxBlocksPerRow, numBlocks);
|
|
||||||
int numRows = (numBlocks + numCols - 1) / numCols;
|
|
||||||
|
|
||||||
// 计算每个块的宽度和高度
|
|
||||||
int blockWidth = colorBlockSize + regionSize + blockMargin * 3;
|
|
||||||
int blockHeight = regionSize + blockMargin + infoHeight;
|
|
||||||
|
|
||||||
// 图片总尺寸
|
|
||||||
int totalWidth = numCols * (blockWidth + blockMargin) + blockMargin;
|
|
||||||
int totalHeight = 100 + (numRows * (blockHeight + blockMargin)) + blockMargin;
|
|
||||||
|
|
||||||
// 创建大图
|
|
||||||
cv::Mat regionImage(totalHeight, totalWidth, CV_8UC3, cv::Scalar(240, 240, 240));
|
|
||||||
|
|
||||||
// 添加标题
|
|
||||||
std::string title = cv::format("Continuous Region %d - Cross-View Comparison", regionId);
|
|
||||||
cv::putText(regionImage, title,
|
|
||||||
cv::Point(20, 30),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 0, 0), 2);
|
|
||||||
|
|
||||||
// 添加区域统计信息
|
|
||||||
std::string regionStats = cv::format("Contains %zu faces, Visible in %zu views",
|
|
||||||
regionInfo.faceIds.size(), regionInfo.viewColors.size());
|
|
||||||
cv::putText(regionImage, regionStats,
|
|
||||||
cv::Point(20, 60),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
std::string gaussStats = cv::format("Region Gaussian Color: R=%d, G=%d, B=%d",
|
|
||||||
regionInfo.regionGaussianColor.r,
|
|
||||||
regionInfo.regionGaussianColor.g,
|
|
||||||
regionInfo.regionGaussianColor.b);
|
|
||||||
cv::putText(regionImage, gaussStats,
|
|
||||||
cv::Point(20, 85),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(255, 0, 0), 1);
|
|
||||||
|
|
||||||
int blockIndex = 0;
|
|
||||||
int currentRow = 0;
|
|
||||||
int currentCol = 0;
|
|
||||||
|
|
||||||
// 计算统计数据
|
|
||||||
float sumDistances = 0.0f;
|
|
||||||
float maxDistance = 0.0f;
|
|
||||||
std::string worstView;
|
|
||||||
|
|
||||||
// 遍历这个区域的所有视图
|
|
||||||
for (const auto& viewEntry : regionInfo.viewColors) {
|
|
||||||
const std::string& viewName = viewEntry.first;
|
|
||||||
const MeshColor& viewColor = viewEntry.second;
|
|
||||||
float colorDistance = regionInfo.viewDistances.at(viewName);
|
|
||||||
|
|
||||||
sumDistances += colorDistance;
|
|
||||||
if (colorDistance > maxDistance) {
|
|
||||||
maxDistance = colorDistance;
|
|
||||||
worstView = viewName;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算当前块的位置
|
|
||||||
int blockX = blockMargin + currentCol * (blockWidth + blockMargin);
|
|
||||||
int blockY = 100 + blockMargin + currentRow * (blockHeight + blockMargin);
|
|
||||||
|
|
||||||
// 绘制块背景
|
|
||||||
cv::rectangle(regionImage,
|
|
||||||
cv::Rect(blockX, blockY, blockWidth, blockHeight),
|
|
||||||
cv::Scalar(255, 255, 255), -1);
|
|
||||||
cv::rectangle(regionImage,
|
|
||||||
cv::Rect(blockX, blockY, blockWidth, blockHeight),
|
|
||||||
cv::Scalar(200, 200, 200), 2);
|
|
||||||
|
|
||||||
// 绘制区域高斯颜色块
|
|
||||||
int gaussianX = blockX + blockMargin;
|
|
||||||
int gaussianY = blockY + blockMargin;
|
|
||||||
|
|
||||||
cv::Scalar gaussianBGR(regionInfo.regionGaussianColor[2],
|
|
||||||
regionInfo.regionGaussianColor[1],
|
|
||||||
regionInfo.regionGaussianColor[0]);
|
|
||||||
cv::rectangle(regionImage,
|
|
||||||
cv::Rect(gaussianX, gaussianY, colorBlockSize, colorBlockSize),
|
|
||||||
gaussianBGR, -1);
|
|
||||||
cv::rectangle(regionImage,
|
|
||||||
cv::Rect(gaussianX, gaussianY, colorBlockSize, colorBlockSize),
|
|
||||||
cv::Scalar(0, 0, 0), 2);
|
|
||||||
|
|
||||||
// 添加高斯标签
|
|
||||||
cv::putText(regionImage, "REGION GAUSS",
|
|
||||||
cv::Point(gaussianX + 5, gaussianY - 5),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.4, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
// 绘制区域图像块
|
|
||||||
int regionX = blockX + blockMargin + colorBlockSize + blockMargin;
|
|
||||||
int regionY = blockY + blockMargin;
|
|
||||||
|
|
||||||
auto visIt = regionInfo.viewVisualizations.find(viewName);
|
|
||||||
if (visIt != regionInfo.viewVisualizations.end() && !visIt->second.empty()) {
|
|
||||||
cv::Mat resizedRegion;
|
|
||||||
cv::resize(visIt->second, resizedRegion, cv::Size(regionSize, regionSize));
|
|
||||||
|
|
||||||
// 将区域图像绘制到指定位置
|
|
||||||
resizedRegion.copyTo(regionImage(cv::Rect(regionX, regionY, regionSize, regionSize)));
|
|
||||||
|
|
||||||
cv::rectangle(regionImage,
|
|
||||||
cv::Rect(regionX, regionY, regionSize, regionSize),
|
|
||||||
cv::Scalar(0, 0, 0), 2);
|
|
||||||
|
|
||||||
// 添加视图标签
|
|
||||||
cv::putText(regionImage, "VIEW REGION",
|
|
||||||
cv::Point(regionX + 5, regionY - 5),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.4, cv::Scalar(0, 0, 0), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 绘制信息区域
|
|
||||||
int infoY = blockY + blockMargin + regionSize;
|
|
||||||
cv::rectangle(regionImage,
|
|
||||||
cv::Rect(blockX, infoY, blockWidth, infoHeight),
|
|
||||||
cv::Scalar(240, 240, 240), -1);
|
|
||||||
cv::rectangle(regionImage,
|
|
||||||
cv::Rect(blockX, infoY, blockWidth, infoHeight),
|
|
||||||
cv::Scalar(200, 200, 200), 1);
|
|
||||||
|
|
||||||
// 添加视图信息
|
|
||||||
std::string displayName = viewName;
|
|
||||||
if (displayName.length() > 20) {
|
|
||||||
displayName = displayName.substr(0, 18) + "...";
|
|
||||||
}
|
|
||||||
|
|
||||||
cv::putText(regionImage, cv::format("View: %s", displayName.c_str()),
|
|
||||||
cv::Point(blockX + 10, infoY + 20),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.4, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
cv::putText(regionImage, cv::format("Region: %d", regionId),
|
|
||||||
cv::Point(blockX + 10, infoY + 40),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.4, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
// 添加颜色值
|
|
||||||
cv::putText(regionImage,
|
|
||||||
cv::format("Region Gauss: (%d,%d,%d)",
|
|
||||||
regionInfo.regionGaussianColor[0],
|
|
||||||
regionInfo.regionGaussianColor[1],
|
|
||||||
regionInfo.regionGaussianColor[2]),
|
|
||||||
cv::Point(blockX + 10, infoY + 60),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.35, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
cv::putText(regionImage,
|
|
||||||
cv::format("View Color: (%d,%d,%d)",
|
|
||||||
viewColor[0], viewColor[1], viewColor[2]),
|
|
||||||
cv::Point(blockX + 10, infoY + 80),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.35, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
// 添加距离和阈值
|
|
||||||
cv::putText(regionImage,
|
|
||||||
cv::format("Distance: %.4f", colorDistance),
|
|
||||||
cv::Point(blockX + 10, infoY + 100),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.35, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
// 添加判断结果
|
|
||||||
int resultX = blockX + blockWidth - 50;
|
|
||||||
int resultY = infoY + 30;
|
|
||||||
|
|
||||||
std::string resultText = (colorDistance > 0.31f) ? "FIX" : "OK";
|
|
||||||
cv::Scalar resultColor = (colorDistance > 0.31f) ? cv::Scalar(0, 0, 255) : cv::Scalar(0, 180, 0);
|
|
||||||
|
|
||||||
cv::putText(regionImage, resultText,
|
|
||||||
cv::Point(resultX, resultY),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.6, resultColor, 1);
|
|
||||||
|
|
||||||
// 更新行和列索引
|
|
||||||
blockIndex++;
|
|
||||||
currentCol++;
|
|
||||||
if (currentCol >= numCols) {
|
|
||||||
currentCol = 0;
|
|
||||||
currentRow++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在底部添加统计信息
|
|
||||||
float avgDistance = sumDistances / regionInfo.viewColors.size();
|
|
||||||
std::string stats = cv::format("Avg Distance: %.4f, Max Distance: %.4f (in %s)",
|
|
||||||
avgDistance, maxDistance, worstView.c_str());
|
|
||||||
cv::putText(regionImage, stats,
|
|
||||||
cv::Point(20, totalHeight - 20),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
// 保存图片
|
|
||||||
std::string outputPath = outputDir + cv::format("continuous_region_%d_comparison.png", regionId);
|
|
||||||
|
|
||||||
if (!cv::imwrite(outputPath, regionImage)) {
|
|
||||||
printf("❌❌ 无法保存连续区域 %d 的对比图: %s\n", regionId, outputPath.c_str());
|
|
||||||
} else {
|
|
||||||
printf("✅ 连续区域 %d 对比图已保存: %s\n", regionId, outputPath.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
regionCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存连续区域信息到CSV文件
|
|
||||||
std::string regionSummaryPath = outputDir + "continuous_regions_summary.csv";
|
|
||||||
FILE* regionFp = fopen(regionSummaryPath.c_str(), "w");
|
|
||||||
|
|
||||||
if (regionFp) {
|
|
||||||
fprintf(regionFp, "RegionID,FaceCount,Views,AvgDistance,MaxDistance,WorstView,NeedsFixCount,NeedsFix%%\n");
|
|
||||||
|
|
||||||
for (const auto& regionEntry : continuousRegions) {
|
|
||||||
int regionId = regionEntry.first;
|
|
||||||
const ContinuousRegionInfo& regionInfo = regionEntry.second;
|
|
||||||
|
|
||||||
float sumDist = 0.0f;
|
|
||||||
float maxDist = 0.0f;
|
|
||||||
int needsFixCount = 0;
|
|
||||||
|
|
||||||
for (const auto& distEntry : regionInfo.viewDistances) {
|
|
||||||
float dist = distEntry.second;
|
|
||||||
sumDist += dist;
|
|
||||||
if (dist > maxDist) maxDist = dist;
|
|
||||||
if (dist > 0.31f) needsFixCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
float avgDist = (regionInfo.viewDistances.size() > 0) ?
|
|
||||||
(sumDist / regionInfo.viewDistances.size()) : 0.0f;
|
|
||||||
float fixPercentage = (regionInfo.viewDistances.size() > 0) ?
|
|
||||||
(needsFixCount * 100.0f / regionInfo.viewDistances.size()) : 0.0f;
|
|
||||||
|
|
||||||
fprintf(regionFp, "%d,%zu,%zu,%.4f,%.4f,",
|
|
||||||
regionId, regionInfo.faceIds.size(), regionInfo.viewDistances.size(),
|
|
||||||
avgDist, maxDist);
|
|
||||||
|
|
||||||
// 找到最差的视图
|
|
||||||
std::string worstView = "";
|
|
||||||
for (const auto& distEntry : regionInfo.viewDistances) {
|
|
||||||
if (distEntry.second == maxDist) {
|
|
||||||
worstView = distEntry.first;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(regionFp, "%s,%d,%.2f%%\n",
|
|
||||||
worstView.c_str(), needsFixCount, fixPercentage);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(regionFp);
|
|
||||||
printf("✅ 连续区域汇总统计信息已保存到: %s\n", regionSummaryPath.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
#ifndef COLORCOMPARISONBFACE_H
|
|
||||||
#define COLORCOMPARISONBFACE_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <opencv2/opencv.hpp>
|
|
||||||
|
|
||||||
// 不使用前向声明,直接定义简单的颜色结构
|
|
||||||
struct MeshColor {
|
|
||||||
unsigned char r, g, b;
|
|
||||||
|
|
||||||
MeshColor() : r(0), g(0), b(0) {}
|
|
||||||
MeshColor(unsigned char r, unsigned char g, unsigned char b)
|
|
||||||
: r(r), g(g), b(b) {}
|
|
||||||
|
|
||||||
unsigned char operator[](size_t idx) const {
|
|
||||||
if (idx == 0) return r;
|
|
||||||
if (idx == 1) return g;
|
|
||||||
if (idx == 2) return b;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ColorComparisonFace {
|
|
||||||
private:
|
|
||||||
// 单面信息结构
|
|
||||||
struct FaceColorInfo {
|
|
||||||
int faceId; // 面的ID
|
|
||||||
MeshColor gaussianColor; // 高斯颜色
|
|
||||||
MeshColor originalColor; // 原始颜色
|
|
||||||
cv::Mat triangleRegion; // 带透明通道的三角形区域
|
|
||||||
cv::Mat visualization; // 三角形可视化图像
|
|
||||||
float colorDistance; // 颜色距离
|
|
||||||
float threshold; // 阈值
|
|
||||||
std::string filename; // 文件名
|
|
||||||
};
|
|
||||||
|
|
||||||
// 连续区域信息结构
|
|
||||||
struct ContinuousRegionInfo {
|
|
||||||
int regionId; // 区域ID
|
|
||||||
std::set<unsigned int> faceIds; // 修改为 unsigned int
|
|
||||||
MeshColor regionGaussianColor; // 区域的整体高斯颜色
|
|
||||||
std::map<std::string, MeshColor> viewColors; // 每个视图的颜色
|
|
||||||
std::map<std::string, cv::Mat> viewRegions; // 每个视图的区域图像
|
|
||||||
std::map<std::string, cv::Mat> viewVisualizations; // 每个视图的可视化
|
|
||||||
std::map<std::string, float> viewDistances; // 每个视图的距离
|
|
||||||
int totalPixels; // 区域总像素数
|
|
||||||
};
|
|
||||||
|
|
||||||
// 使用嵌套map: faceid -> filename -> FaceColorInfo列表
|
|
||||||
std::map<int, std::map<std::string, std::vector<FaceColorInfo>>> faceViewColorMap;
|
|
||||||
|
|
||||||
// 连续区域映射: regionId -> ContinuousRegionInfo
|
|
||||||
std::map<int, ContinuousRegionInfo> continuousRegions;
|
|
||||||
|
|
||||||
std::string outputDir;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// 构造函数
|
|
||||||
ColorComparisonFace(const std::string& dir);
|
|
||||||
|
|
||||||
// 析构函数
|
|
||||||
virtual ~ColorComparisonFace() {}
|
|
||||||
|
|
||||||
// 添加精确三角形信息(替代原来的addColorInfo)
|
|
||||||
void addExactTriangleInfo(int faceId,
|
|
||||||
MeshColor gaussianColor,
|
|
||||||
MeshColor originalColor,
|
|
||||||
const cv::Mat& triangleRegionWithAlpha, // 带透明通道
|
|
||||||
const cv::Mat& visualization, // 可视化图像
|
|
||||||
float colorDistance,
|
|
||||||
float threshold,
|
|
||||||
const std::string& filename);
|
|
||||||
|
|
||||||
// 添加连续区域信息
|
|
||||||
void addContinuousRegionInfo(int regionId,
|
|
||||||
const std::set<unsigned int>& faceIds, // 修改为 unsigned int
|
|
||||||
MeshColor regionGaussianColor);
|
|
||||||
|
|
||||||
// 添加连续区域在特定视图中的信息
|
|
||||||
void addRegionViewInfo(int regionId,
|
|
||||||
const std::string& filename,
|
|
||||||
MeshColor viewColor,
|
|
||||||
const cv::Mat& regionImage,
|
|
||||||
const cv::Mat& visualization,
|
|
||||||
float colorDistance);
|
|
||||||
|
|
||||||
// 创建三角形区域对比图
|
|
||||||
void createBatchComparison(int maxBlocksPerRow = 6, int maxFacesPerImage = 20);
|
|
||||||
|
|
||||||
// 创建连续区域跨视图比较图
|
|
||||||
void createContinuousRegionComparison(int maxBlocksPerRow = 6, int maxRegionsPerImage = 20);
|
|
||||||
|
|
||||||
// 保存颜色信息到CSV文件
|
|
||||||
void saveColorInfoToFile();
|
|
||||||
|
|
||||||
// 获取总face数
|
|
||||||
int getTotalFaces() const;
|
|
||||||
|
|
||||||
// 获取总记录数
|
|
||||||
int getTotalRecords() const;
|
|
||||||
|
|
||||||
// 获取faceid列表
|
|
||||||
std::vector<int> getFaceIds() const;
|
|
||||||
|
|
||||||
// 获取连续区域数
|
|
||||||
int getTotalRegions() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // COLORCOMPARISONBFACE_H
|
|
||||||
@@ -1,505 +0,0 @@
|
|||||||
#include "ColorComparisonPixel.h"
|
|
||||||
#include "Mesh.h" // 在cpp中包含完整定义
|
|
||||||
#include <opencv2/opencv.hpp>
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
using namespace MVS;
|
|
||||||
|
|
||||||
// 辅助函数:检查目录是否存在
|
|
||||||
static bool directoryExists(const std::string& path) {
|
|
||||||
struct stat info;
|
|
||||||
if (stat(path.c_str(), &info) != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (info.st_mode & S_IFDIR) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 辅助函数:创建目录
|
|
||||||
static bool createDirectory(const std::string& path) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
return _mkdir(path.c_str()) == 0;
|
|
||||||
#else
|
|
||||||
return mkdir(path.c_str(), 0755) == 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// 辅助函数:递归创建目录
|
|
||||||
static bool createDirectories(const std::string& path) {
|
|
||||||
size_t pos = 0;
|
|
||||||
std::string dir;
|
|
||||||
|
|
||||||
while ((pos = path.find_first_of("/\\", pos + 1)) != std::string::npos) {
|
|
||||||
dir = path.substr(0, pos);
|
|
||||||
if (!directoryExists(dir)) {
|
|
||||||
if (!createDirectory(dir)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建最终目录
|
|
||||||
if (!directoryExists(path)) {
|
|
||||||
return createDirectory(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构造函数
|
|
||||||
ColorComparisonPixel::ColorComparisonPixel(const std::string& dir) : outputDir(dir) {
|
|
||||||
// 确保输出目录以斜杠结尾
|
|
||||||
if (!outputDir.empty() && outputDir.back() != '/' && outputDir.back() != '\\') {
|
|
||||||
outputDir += '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建输出目录
|
|
||||||
if (!createDirectories(outputDir)) {
|
|
||||||
printf("⚠️ 无法创建目录: %s\n", outputDir.c_str());
|
|
||||||
} else {
|
|
||||||
printf("✅ 输出目录: %s\n", outputDir.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加颜色信息
|
|
||||||
void ColorComparisonPixel::addColorInfo(int faceId,
|
|
||||||
const MeshColor& gaussianColor,
|
|
||||||
const MeshColor& originalColor,
|
|
||||||
float distance, float threshold,
|
|
||||||
const std::string& filename) {
|
|
||||||
ColorInfo info = {faceId, gaussianColor, originalColor, distance, threshold, filename};
|
|
||||||
faceViewColorMap[faceId][filename].push_back(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取总face数
|
|
||||||
int ColorComparisonPixel::getTotalFaces() const {
|
|
||||||
return faceViewColorMap.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取总记录数
|
|
||||||
int ColorComparisonPixel::getTotalRecords() const {
|
|
||||||
int total = 0;
|
|
||||||
for (const auto& faceEntry : faceViewColorMap) {
|
|
||||||
for (const auto& viewEntry : faceEntry.second) {
|
|
||||||
total += viewEntry.second.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取faceid列表
|
|
||||||
std::vector<int> ColorComparisonPixel::getFaceIds() const {
|
|
||||||
std::vector<int> faceIds;
|
|
||||||
for (const auto& faceEntry : faceViewColorMap) {
|
|
||||||
faceIds.push_back(faceEntry.first);
|
|
||||||
}
|
|
||||||
return faceIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建按faceid分组的对比图
|
|
||||||
void ColorComparisonPixel::createBatchComparison(int maxCellsPerRow, int maxFacesPerImage) {
|
|
||||||
if (faceViewColorMap.empty()) {
|
|
||||||
printf("⚠️ 没有颜色信息可生成\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("正在创建按faceid分组的对比图...\n");
|
|
||||||
printf("总face数: %zu\n", faceViewColorMap.size());
|
|
||||||
printf("总记录数: %d\n", getTotalRecords());
|
|
||||||
|
|
||||||
// 单元格参数
|
|
||||||
int cellWidth = 220; // 每个单元格宽度
|
|
||||||
int cellHeight = 150; // 每个单元格高度
|
|
||||||
int cellMargin = 5; // 单元格边距
|
|
||||||
|
|
||||||
// 左侧视图名称区域宽度
|
|
||||||
int viewNameWidth = 200;
|
|
||||||
|
|
||||||
// 限制处理的face数
|
|
||||||
int facesToProcess = std::min((int)faceViewColorMap.size(), maxFacesPerImage);
|
|
||||||
|
|
||||||
// 处理前N个face
|
|
||||||
int faceCount = 0;
|
|
||||||
for (const auto& faceEntry : faceViewColorMap) {
|
|
||||||
if (faceCount >= facesToProcess) break;
|
|
||||||
|
|
||||||
int faceId = faceEntry.first;
|
|
||||||
const auto& viewMap = faceEntry.second;
|
|
||||||
|
|
||||||
printf("处理 face %d (%zu 个视图)...\n", faceId, viewMap.size());
|
|
||||||
|
|
||||||
// 计算这个face需要的总行数
|
|
||||||
int totalRows = 0;
|
|
||||||
std::vector<std::pair<std::string, int>> viewRowsInfo; // 存储每个视图需要的行数
|
|
||||||
|
|
||||||
for (const auto& viewEntry : viewMap) {
|
|
||||||
const std::string& viewName = viewEntry.first;
|
|
||||||
const std::vector<ColorInfo>& infos = viewEntry.second;
|
|
||||||
|
|
||||||
// 计算这个视图需要的行数
|
|
||||||
int rowsForThisView = (infos.size() + maxCellsPerRow - 1) / maxCellsPerRow;
|
|
||||||
viewRowsInfo.emplace_back(viewName, rowsForThisView);
|
|
||||||
totalRows += rowsForThisView;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (totalRows == 0) continue;
|
|
||||||
|
|
||||||
// 图片总尺寸
|
|
||||||
int totalWidth = viewNameWidth + (maxCellsPerRow * cellWidth);
|
|
||||||
int totalHeight = 60 + (totalRows * cellHeight); // 60像素用于标题
|
|
||||||
|
|
||||||
// 创建大图
|
|
||||||
cv::Mat faceImage(totalHeight, totalWidth, CV_8UC3, cv::Scalar(245, 245, 245));
|
|
||||||
|
|
||||||
// 添加标题
|
|
||||||
std::string title = cv::format("Face %d - Color Comparison", faceId);
|
|
||||||
cv::putText(faceImage, title,
|
|
||||||
cv::Point(20, 30),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(0, 0, 0), 2);
|
|
||||||
|
|
||||||
// 添加统计信息
|
|
||||||
int totalRecords = 0;
|
|
||||||
int needFixCount = 0;
|
|
||||||
for (const auto& viewEntry : viewMap) {
|
|
||||||
totalRecords += viewEntry.second.size();
|
|
||||||
for (const auto& info : viewEntry.second) {
|
|
||||||
if (info.distance > info.threshold) needFixCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string stats = cv::format("Views: %zu, Records: %d, Need Fix: %d",
|
|
||||||
viewMap.size(), totalRecords, needFixCount);
|
|
||||||
cv::putText(faceImage, stats,
|
|
||||||
cv::Point(20, 55),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
int currentGlobalRow = 0; // 全局行索引
|
|
||||||
|
|
||||||
// 遍历这个face的所有视图
|
|
||||||
for (const auto& viewRowPair : viewRowsInfo) {
|
|
||||||
const std::string& viewName = viewRowPair.first;
|
|
||||||
int rowsForThisView = viewRowPair.second;
|
|
||||||
const std::vector<ColorInfo>& infos = viewMap.at(viewName);
|
|
||||||
|
|
||||||
// 为这个视图的每一行绘制
|
|
||||||
for (int viewRow = 0; viewRow < rowsForThisView; viewRow++) {
|
|
||||||
int globalRowY = 60 + (currentGlobalRow * cellHeight);
|
|
||||||
|
|
||||||
// 绘制视图名称区域(只在第一行显示)
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Rect(0, globalRowY, viewNameWidth, cellHeight),
|
|
||||||
cv::Scalar(255, 255, 255), -1);
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Rect(0, globalRowY, viewNameWidth, cellHeight),
|
|
||||||
cv::Scalar(200, 200, 200), 1);
|
|
||||||
|
|
||||||
if (viewRow == 0) {
|
|
||||||
// 第一行:显示完整视图信息
|
|
||||||
cv::putText(faceImage, viewName,
|
|
||||||
cv::Point(10, globalRowY + 25),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
std::string countText = cv::format("Records: %zu", infos.size());
|
|
||||||
cv::putText(faceImage, countText,
|
|
||||||
cv::Point(10, globalRowY + 50),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.4, cv::Scalar(100, 100, 100), 1);
|
|
||||||
|
|
||||||
// 计算这个视图中需要修复的记录数
|
|
||||||
int viewNeedFixCount = 0;
|
|
||||||
for (const auto& info : infos) {
|
|
||||||
if (info.distance > info.threshold) viewNeedFixCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (viewNeedFixCount > 0) {
|
|
||||||
std::string fixText = cv::format("Fix: %d", viewNeedFixCount);
|
|
||||||
cv::Scalar fixColor = cv::Scalar(0, 0, 255);
|
|
||||||
cv::putText(faceImage, fixText,
|
|
||||||
cv::Point(10, globalRowY + 70),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.4, fixColor, 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 后续行:显示视图名称缩写和行号
|
|
||||||
std::string continuationText = cv::format("%s (cont.)", viewName.substr(0, 8).c_str());
|
|
||||||
cv::putText(faceImage, continuationText,
|
|
||||||
cv::Point(10, globalRowY + 25),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.4, cv::Scalar(100, 100, 100), 1);
|
|
||||||
|
|
||||||
std::string rowText = cv::format("Row %d/%d", viewRow + 1, rowsForThisView);
|
|
||||||
cv::putText(faceImage, rowText,
|
|
||||||
cv::Point(10, globalRowY + 50),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.4, cv::Scalar(100, 100, 100), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算这一行要显示的记录
|
|
||||||
int startIndex = viewRow * maxCellsPerRow;
|
|
||||||
int endIndex = std::min(startIndex + maxCellsPerRow, (int)infos.size());
|
|
||||||
|
|
||||||
// 绘制这一行的单元格
|
|
||||||
for (int col = 0; col < (endIndex - startIndex); col++) {
|
|
||||||
int infoIndex = startIndex + col;
|
|
||||||
const ColorInfo& info = infos[infoIndex];
|
|
||||||
|
|
||||||
// 计算单元格位置
|
|
||||||
int cellX = viewNameWidth + (col * cellWidth);
|
|
||||||
int cellY = globalRowY;
|
|
||||||
|
|
||||||
// 绘制单个单元格
|
|
||||||
int innerX = cellX + cellMargin;
|
|
||||||
int innerY = cellY + cellMargin;
|
|
||||||
int innerWidth = cellWidth - 2 * cellMargin;
|
|
||||||
int innerHeight = cellHeight - 2 * cellMargin;
|
|
||||||
|
|
||||||
// 创建单元格背景
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Rect(cellX, cellY, cellWidth, cellHeight),
|
|
||||||
cv::Scalar(255, 255, 255), -1);
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Rect(cellX, cellY, cellWidth, cellHeight),
|
|
||||||
cv::Scalar(200, 200, 200), 1);
|
|
||||||
|
|
||||||
// 颜色块参数
|
|
||||||
int colorBlockSize = 35;
|
|
||||||
int colorBlockY = innerY + 10;
|
|
||||||
|
|
||||||
// 绘制高斯颜色块
|
|
||||||
cv::Scalar gaussianBGR(info.gaussianColor[2], info.gaussianColor[1], info.gaussianColor[0]);
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Point(innerX + 5, colorBlockY),
|
|
||||||
cv::Point(innerX + 5 + colorBlockSize, colorBlockY + colorBlockSize),
|
|
||||||
gaussianBGR, -1);
|
|
||||||
|
|
||||||
// 绘制原始颜色块
|
|
||||||
cv::Scalar originalBGR(info.originalColor[2], info.originalColor[1], info.originalColor[0]);
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Point(innerX + 5 + colorBlockSize + 35, colorBlockY),
|
|
||||||
cv::Point(innerX + 5 + colorBlockSize + 35 + colorBlockSize, colorBlockY + colorBlockSize),
|
|
||||||
originalBGR, -1);
|
|
||||||
|
|
||||||
// 添加边框
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Point(innerX + 5, colorBlockY),
|
|
||||||
cv::Point(innerX + 5 + colorBlockSize, colorBlockY + colorBlockSize),
|
|
||||||
cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Point(innerX + 5 + colorBlockSize + 35, colorBlockY),
|
|
||||||
cv::Point(innerX + 5 + colorBlockSize + 35 + colorBlockSize, colorBlockY + colorBlockSize),
|
|
||||||
cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
// 添加标签
|
|
||||||
int labelY = colorBlockY - 5;
|
|
||||||
cv::putText(faceImage, "G",
|
|
||||||
cv::Point(innerX + 5 + 10, labelY),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.4, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
cv::putText(faceImage, "O",
|
|
||||||
cv::Point(innerX + 5 + colorBlockSize + 35 + 10, labelY),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.4, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
// 添加颜色值(简化显示)
|
|
||||||
std::string gValue = cv::format("%d,%d,%d",
|
|
||||||
info.gaussianColor[0], info.gaussianColor[1], info.gaussianColor[2]);
|
|
||||||
cv::putText(faceImage, gValue,
|
|
||||||
cv::Point(innerX + 5, innerY + 60),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.3, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
std::string oValue = cv::format("%d,%d,%d",
|
|
||||||
info.originalColor[0], info.originalColor[1], info.originalColor[2]);
|
|
||||||
cv::putText(faceImage, oValue,
|
|
||||||
cv::Point(innerX + 5 + colorBlockSize + 35, innerY + 60),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.3, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
// 添加距离
|
|
||||||
std::string distText = cv::format("D:%.3f", info.distance);
|
|
||||||
cv::putText(faceImage, distText,
|
|
||||||
cv::Point(innerX + 5, innerY + 75),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.35, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
// 添加阈值
|
|
||||||
std::string threshText = cv::format("T:%.3f", info.threshold);
|
|
||||||
cv::putText(faceImage, threshText,
|
|
||||||
cv::Point(innerX + 5 + colorBlockSize + 35, innerY + 75),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.35, cv::Scalar(0, 0, 0), 1);
|
|
||||||
|
|
||||||
// 添加判断结果
|
|
||||||
std::string resultText = (info.distance > info.threshold) ? "FIX" : "OK";
|
|
||||||
cv::Scalar resultColor = (info.distance > info.threshold) ? cv::Scalar(0, 0, 255) : cv::Scalar(0, 180, 0);
|
|
||||||
|
|
||||||
cv::putText(faceImage, resultText,
|
|
||||||
cv::Point(innerX + 5, innerY + 90),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.4, resultColor, 1);
|
|
||||||
|
|
||||||
// 添加记录序号
|
|
||||||
std::string seqText = cv::format("#%d", infoIndex + 1);
|
|
||||||
cv::putText(faceImage, seqText,
|
|
||||||
cv::Point(innerX + 5, innerY + 110),
|
|
||||||
cv::FONT_HERSHEY_SIMPLEX, 0.3, cv::Scalar(100, 100, 100), 1);
|
|
||||||
|
|
||||||
// 添加判断标记
|
|
||||||
if (info.distance > info.threshold) {
|
|
||||||
cv::circle(faceImage,
|
|
||||||
cv::Point(innerX + innerWidth - 10, innerY + 10),
|
|
||||||
5, cv::Scalar(0, 0, 255), -1);
|
|
||||||
} else {
|
|
||||||
cv::circle(faceImage,
|
|
||||||
cv::Point(innerX + innerWidth - 10, innerY + 10),
|
|
||||||
5, cv::Scalar(0, 180, 0), -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果这一行没有填满,留空
|
|
||||||
for (int col = (endIndex - startIndex); col < maxCellsPerRow; col++) {
|
|
||||||
int cellX = viewNameWidth + (col * cellWidth);
|
|
||||||
int cellY = globalRowY;
|
|
||||||
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Rect(cellX, cellY, cellWidth, cellHeight),
|
|
||||||
cv::Scalar(245, 245, 245), -1);
|
|
||||||
cv::rectangle(faceImage,
|
|
||||||
cv::Rect(cellX, cellY, cellWidth, cellHeight),
|
|
||||||
cv::Scalar(220, 220, 220), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentGlobalRow++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在每个视图之后添加一个浅色分隔线
|
|
||||||
if (currentGlobalRow < totalRows) {
|
|
||||||
int separatorY = 60 + (currentGlobalRow * cellHeight);
|
|
||||||
cv::line(faceImage,
|
|
||||||
cv::Point(0, separatorY - 1),
|
|
||||||
cv::Point(totalWidth, separatorY - 1),
|
|
||||||
cv::Scalar(220, 220, 220), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加网格线
|
|
||||||
for (int row = 0; row <= totalRows; row++) {
|
|
||||||
int y = 60 + (row * cellHeight);
|
|
||||||
cv::line(faceImage,
|
|
||||||
cv::Point(0, y),
|
|
||||||
cv::Point(totalWidth, y),
|
|
||||||
cv::Scalar(200, 200, 200), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加视图名称区域分隔线
|
|
||||||
cv::line(faceImage,
|
|
||||||
cv::Point(viewNameWidth, 60),
|
|
||||||
cv::Point(viewNameWidth, totalHeight),
|
|
||||||
cv::Scalar(150, 150, 150), 2);
|
|
||||||
|
|
||||||
// 保存图片
|
|
||||||
std::string outputPath = outputDir + cv::format("face_%d_comparison.png", faceId);
|
|
||||||
|
|
||||||
if (!cv::imwrite(outputPath, faceImage)) {
|
|
||||||
printf("❌ 无法保存face %d的对比图: %s\n", faceId, outputPath.c_str());
|
|
||||||
} else {
|
|
||||||
printf("✅ face %d对比图已保存: %s\n", faceId, outputPath.c_str());
|
|
||||||
printf(" 尺寸: %d x %d 像素, 视图数: %zu, 记录数: %d\n",
|
|
||||||
totalWidth, totalHeight, viewMap.size(), totalRecords);
|
|
||||||
}
|
|
||||||
|
|
||||||
faceCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存颜色信息到CSV文件
|
|
||||||
saveColorInfoToFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存颜色信息到CSV文件
|
|
||||||
void ColorComparisonPixel::saveColorInfoToFile() {
|
|
||||||
// 为每个face保存单独的CSV
|
|
||||||
for (const auto& faceEntry : faceViewColorMap) {
|
|
||||||
int faceId = faceEntry.first;
|
|
||||||
const auto& viewMap = faceEntry.second;
|
|
||||||
|
|
||||||
if (viewMap.empty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string filePath = outputDir + cv::format("face_%d_colors.csv", faceId);
|
|
||||||
FILE* fp = fopen(filePath.c_str(), "w");
|
|
||||||
|
|
||||||
if (!fp) {
|
|
||||||
printf("❌ 无法创建文件: %s\n", filePath.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 写入CSV标题
|
|
||||||
fprintf(fp, "FaceID,View,RecordNo,Gaussian_R,Gaussian_G,Gaussian_B,Original_R,Original_G,Original_B,Distance,Threshold,NeedsFix\n");
|
|
||||||
|
|
||||||
int recordNo = 1;
|
|
||||||
for (const auto& viewEntry : viewMap) {
|
|
||||||
const std::string& viewName = viewEntry.first;
|
|
||||||
const std::vector<ColorInfo>& infos = viewEntry.second;
|
|
||||||
|
|
||||||
for (int i = 0; i < (int)infos.size(); i++) {
|
|
||||||
const ColorInfo& info = infos[i];
|
|
||||||
bool needsFix = (info.distance > info.threshold);
|
|
||||||
fprintf(fp, "%d,%s,%d,%d,%d,%d,%d,%d,%d,%.4f,%.4f,%s\n",
|
|
||||||
faceId,
|
|
||||||
viewName.c_str(),
|
|
||||||
i + 1,
|
|
||||||
info.gaussianColor[0], info.gaussianColor[1], info.gaussianColor[2],
|
|
||||||
info.originalColor[0], info.originalColor[1], info.originalColor[2],
|
|
||||||
info.distance, info.threshold,
|
|
||||||
needsFix ? "YES" : "NO");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
printf("✅ face %d颜色信息已保存到: %s\n", faceId, filePath.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存汇总统计信息
|
|
||||||
std::string summaryPath = outputDir + "face_comparison_summary.csv";
|
|
||||||
FILE* summaryFp = fopen(summaryPath.c_str(), "w");
|
|
||||||
|
|
||||||
if (summaryFp) {
|
|
||||||
fprintf(summaryFp, "FaceID,Views,TotalRecords,NeedFixRecords,NeedFix%%,MinDistance,MaxDistance,AvgDistance\n");
|
|
||||||
|
|
||||||
for (const auto& faceEntry : faceViewColorMap) {
|
|
||||||
int faceId = faceEntry.first;
|
|
||||||
const auto& viewMap = faceEntry.second;
|
|
||||||
|
|
||||||
int totalRecords = 0;
|
|
||||||
int needFixCount = 0;
|
|
||||||
float minDist = std::numeric_limits<float>::max();
|
|
||||||
float maxDist = 0.0f;
|
|
||||||
float sumDist = 0.0f;
|
|
||||||
|
|
||||||
for (const auto& viewEntry : viewMap) {
|
|
||||||
const std::vector<ColorInfo>& infos = viewEntry.second;
|
|
||||||
totalRecords += infos.size();
|
|
||||||
|
|
||||||
for (const auto& info : infos) {
|
|
||||||
if (info.distance > info.threshold) needFixCount++;
|
|
||||||
minDist = std::min(minDist, info.distance);
|
|
||||||
maxDist = std::max(maxDist, info.distance);
|
|
||||||
sumDist += info.distance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float avgDist = (totalRecords > 0) ? (sumDist / totalRecords) : 0.0f;
|
|
||||||
float fixPercentage = (totalRecords > 0) ? (needFixCount * 100.0f / totalRecords) : 0.0f;
|
|
||||||
|
|
||||||
fprintf(summaryFp, "%d,%zu,%d,%d,%.2f%%,%.4f,%.4f,%.4f\n",
|
|
||||||
faceId,
|
|
||||||
viewMap.size(),
|
|
||||||
totalRecords,
|
|
||||||
needFixCount,
|
|
||||||
fixPercentage,
|
|
||||||
minDist,
|
|
||||||
maxDist,
|
|
||||||
avgDist);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(summaryFp);
|
|
||||||
printf("✅ 汇总统计信息已保存到: %s\n", summaryPath.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
#ifndef COLORCOMPARISONPIXEL_H
|
|
||||||
#define COLORCOMPARISONPIXEL_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include "ColorComparisonFace.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
// 不使用前向声明,直接定义简单的颜色结构
|
|
||||||
struct MeshColor {
|
|
||||||
unsigned char r, g, b;
|
|
||||||
|
|
||||||
MeshColor() : r(0), g(0), b(0) {}
|
|
||||||
MeshColor(unsigned char r, unsigned char g, unsigned char b)
|
|
||||||
: r(r), g(g), b(b) {}
|
|
||||||
|
|
||||||
unsigned char operator[](size_t idx) const {
|
|
||||||
if (idx == 0) return r;
|
|
||||||
if (idx == 1) return g;
|
|
||||||
if (idx == 2) return b;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
class ColorComparisonPixel {
|
|
||||||
private:
|
|
||||||
struct ColorInfo {
|
|
||||||
int faceId;
|
|
||||||
MeshColor gaussianColor;
|
|
||||||
MeshColor originalColor;
|
|
||||||
float distance;
|
|
||||||
float threshold;
|
|
||||||
std::string filename;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 使用嵌套map: faceid -> filename -> ColorInfo列表
|
|
||||||
std::map<int, std::map<std::string, std::vector<ColorInfo>>> faceViewColorMap;
|
|
||||||
std::string outputDir;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// 构造函数
|
|
||||||
ColorComparisonPixel(const std::string& dir);
|
|
||||||
|
|
||||||
// 析构函数
|
|
||||||
virtual ~ColorComparisonPixel() {}
|
|
||||||
|
|
||||||
// 添加颜色信息
|
|
||||||
void addColorInfo(int faceId,
|
|
||||||
const MeshColor& gaussianColor,
|
|
||||||
const MeshColor& originalColor,
|
|
||||||
float distance, float threshold,
|
|
||||||
const std::string& filename);
|
|
||||||
|
|
||||||
// 创建按faceid分组的对比图
|
|
||||||
void createBatchComparison(int maxCellsPerRow = 10, int maxFacesPerImage = 50);
|
|
||||||
|
|
||||||
// 保存颜色信息到CSV文件
|
|
||||||
void saveColorInfoToFile();
|
|
||||||
|
|
||||||
// 获取总face数
|
|
||||||
int getTotalFaces() const;
|
|
||||||
|
|
||||||
// 获取总记录数
|
|
||||||
int getTotalRecords() const;
|
|
||||||
|
|
||||||
// 获取faceid列表
|
|
||||||
std::vector<int> getFaceIds() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // COLORCOMPARISONPIXEL_H
|
|
||||||
+8
-37
@@ -350,16 +350,6 @@ void Mesh::ComputeNormalFaces()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute color for all faces
|
|
||||||
void Mesh::ComputeColorFaces()
|
|
||||||
{
|
|
||||||
faceColors.resize(faces.size());
|
|
||||||
|
|
||||||
FOREACH(idxFace, faces)
|
|
||||||
faceColors[idxFace] = FaceColor(faces[idxFace]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// compute normal for all vertices
|
// compute normal for all vertices
|
||||||
#if 1
|
#if 1
|
||||||
// computes the vertex normal as the area weighted face normals average
|
// computes the vertex normal as the area weighted face normals average
|
||||||
@@ -1106,20 +1096,18 @@ namespace BasicPLY {
|
|||||||
struct Vertex {
|
struct Vertex {
|
||||||
Mesh::Vertex v;
|
Mesh::Vertex v;
|
||||||
Mesh::Normal n;
|
Mesh::Normal n;
|
||||||
Mesh::Color color;
|
|
||||||
static void InitLoadProps(PLY& ply, int elem_count,
|
static void InitLoadProps(PLY& ply, int elem_count,
|
||||||
Mesh::VertexArr& vertices, Mesh::ColorArr& vertexColors)
|
Mesh::VertexArr& vertices, Mesh::NormalArr& vertexNormals)
|
||||||
{
|
{
|
||||||
PLY::PlyElement* elm = ply.find_element(elem_names[0]);
|
PLY::PlyElement* elm = ply.find_element(elem_names[0]);
|
||||||
const size_t nMaxProps(SizeOfArray(props));
|
const size_t nMaxProps(SizeOfArray(props));
|
||||||
for (size_t p=0; p<nMaxProps; ++p) {
|
for (size_t p=0; p<nMaxProps; ++p) {
|
||||||
// DEBUG_EXTRA("InitLoadProps name=%s\n", props[p].name.c_str());
|
|
||||||
if (ply.find_property(elm, props[p].name.c_str()) < 0)
|
if (ply.find_property(elm, props[p].name.c_str()) < 0)
|
||||||
continue;
|
continue;
|
||||||
ply.setup_property(props[p]);
|
ply.setup_property(props[p]);
|
||||||
switch (p) {
|
switch (p) {
|
||||||
case 0: vertices.resize((IDX)elem_count); break;
|
case 0: vertices.resize((IDX)elem_count); break;
|
||||||
case 3: vertexColors.resize((IDX)elem_count); break;
|
case 3: vertexNormals.resize((IDX)elem_count); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1137,15 +1125,6 @@ namespace BasicPLY {
|
|||||||
}
|
}
|
||||||
static const PLY::PlyProperty props[9];
|
static const PLY::PlyProperty props[9];
|
||||||
};
|
};
|
||||||
const PLY::PlyProperty Vertex::props[] = {
|
|
||||||
{"x", PLY::Float32, PLY::Float32, offsetof(Vertex,v.x), 0, 0, 0, 0},
|
|
||||||
{"y", PLY::Float32, PLY::Float32, offsetof(Vertex,v.y), 0, 0, 0, 0},
|
|
||||||
{"z", PLY::Float32, PLY::Float32, offsetof(Vertex,v.z), 0, 0, 0, 0},
|
|
||||||
{"red", PLY::Int8, PLY::Int8, offsetof(Vertex,color.r), 0, 0, 0, 0},
|
|
||||||
{"green", PLY::Int8, PLY::Int8, offsetof(Vertex,color.g), 0, 0, 0, 0},
|
|
||||||
{"blue", PLY::Int8, PLY::Int8, offsetof(Vertex,color.b), 0, 0, 0, 0}
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
const PLY::PlyProperty Vertex::props[] = {
|
const PLY::PlyProperty Vertex::props[] = {
|
||||||
{"x", PLY::Float32, PLY::Float32, offsetof(Vertex,v.x), 0, 0, 0, 0},
|
{"x", PLY::Float32, PLY::Float32, offsetof(Vertex,v.x), 0, 0, 0, 0},
|
||||||
{"y", PLY::Float32, PLY::Float32, offsetof(Vertex,v.y), 0, 0, 0, 0},
|
{"y", PLY::Float32, PLY::Float32, offsetof(Vertex,v.y), 0, 0, 0, 0},
|
||||||
@@ -1154,7 +1133,6 @@ namespace BasicPLY {
|
|||||||
{"ny", PLY::Float32, PLY::Float32, offsetof(Vertex,n.y), 0, 0, 0, 0},
|
{"ny", PLY::Float32, PLY::Float32, offsetof(Vertex,n.y), 0, 0, 0, 0},
|
||||||
{"nz", PLY::Float32, PLY::Float32, offsetof(Vertex,n.z), 0, 0, 0, 0}
|
{"nz", PLY::Float32, PLY::Float32, offsetof(Vertex,n.z), 0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
*/
|
|
||||||
// list of property information for a face
|
// list of property information for a face
|
||||||
struct Face {
|
struct Face {
|
||||||
struct FaceIndices {
|
struct FaceIndices {
|
||||||
@@ -1213,7 +1191,7 @@ namespace BasicPLY {
|
|||||||
} // namespace MeshInternal
|
} // namespace MeshInternal
|
||||||
|
|
||||||
// import the mesh from the given file
|
// import the mesh from the given file
|
||||||
bool Mesh::Load(const String& fileName, bool *pBExistVertexColor)
|
bool Mesh::Load(const String& fileName)
|
||||||
{
|
{
|
||||||
TD_TIMER_STARTD();
|
TD_TIMER_STARTD();
|
||||||
const String ext(Util::getFileExt(fileName).ToLower());
|
const String ext(Util::getFileExt(fileName).ToLower());
|
||||||
@@ -1224,14 +1202,14 @@ bool Mesh::Load(const String& fileName, bool *pBExistVertexColor)
|
|||||||
if (ext == _T(".gltf") || ext == _T(".glb"))
|
if (ext == _T(".gltf") || ext == _T(".glb"))
|
||||||
ret = LoadGLTF(fileName, ext == _T(".glb"));
|
ret = LoadGLTF(fileName, ext == _T(".glb"));
|
||||||
else
|
else
|
||||||
ret = LoadPLY(fileName, pBExistVertexColor);
|
ret = LoadPLY(fileName);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return false;
|
return false;
|
||||||
DEBUG_EXTRA("Mesh loaded: %u vertices, %u faces (%s)", vertices.size(), faces.size(), TD_TIMER_GET_FMT().c_str());
|
DEBUG_EXTRA("Mesh loaded: %u vertices, %u faces (%s)", vertices.size(), faces.size(), TD_TIMER_GET_FMT().c_str());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// import the mesh as a PLY file
|
// import the mesh as a PLY file
|
||||||
bool Mesh::LoadPLY(const String& fileName, bool *pBExistVertexColor)
|
bool Mesh::LoadPLY(const String& fileName)
|
||||||
{
|
{
|
||||||
ASSERT(!fileName.empty());
|
ASSERT(!fileName.empty());
|
||||||
Release();
|
Release();
|
||||||
@@ -1266,23 +1244,16 @@ bool Mesh::LoadPLY(const String& fileName, bool *pBExistVertexColor)
|
|||||||
LPCSTR elem_name = ply.setup_element_read(i, &elem_count);
|
LPCSTR elem_name = ply.setup_element_read(i, &elem_count);
|
||||||
if (PLY::equal_strings(BasicPLY::elem_names[0], elem_name)) {
|
if (PLY::equal_strings(BasicPLY::elem_names[0], elem_name)) {
|
||||||
ASSERT(vertices.size() == (VIndex)elem_count);
|
ASSERT(vertices.size() == (VIndex)elem_count);
|
||||||
// BasicPLY::Vertex::InitLoadProps(ply, elem_count, vertices, vertexNormals);
|
BasicPLY::Vertex::InitLoadProps(ply, elem_count, vertices, vertexNormals);
|
||||||
BasicPLY::Vertex::InitLoadProps(ply, elem_count, vertices, vertexColors);
|
if (vertexNormals.empty()) {
|
||||||
if (vertexColors.empty()) {
|
|
||||||
// printf("vertexColors.empty, elem_count=%u\n", elem_count);
|
|
||||||
*pBExistVertexColor = false;
|
|
||||||
for (Vertex& vert: vertices)
|
for (Vertex& vert: vertices)
|
||||||
ply.get_element(&vert);
|
ply.get_element(&vert);
|
||||||
} else {
|
} else {
|
||||||
// printf("Not vertexColors.empty, elem_count=%u\n", elem_count);
|
|
||||||
*pBExistVertexColor = true;
|
|
||||||
BasicPLY::Vertex vertex;
|
BasicPLY::Vertex vertex;
|
||||||
for (int v=0; v<elem_count; ++v) {
|
for (int v=0; v<elem_count; ++v) {
|
||||||
ply.get_element(&vertex);
|
ply.get_element(&vertex);
|
||||||
vertices[v] = vertex.v;
|
vertices[v] = vertex.v;
|
||||||
vertexColors[v] = vertex.color;
|
vertexNormals[v] = vertex.n;
|
||||||
// if (v<10)
|
|
||||||
// printf("vertexColors v=%u, rgb=(%u, %u, %u)\n", v, vertex.color.r, vertex.color.g, vertex.color.b);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
|
|||||||
+2
-14
@@ -61,9 +61,6 @@ public:
|
|||||||
typedef SEACAVE::cList<Vertex,const Vertex&,0,8192,VIndex> VertexArr;
|
typedef SEACAVE::cList<Vertex,const Vertex&,0,8192,VIndex> VertexArr;
|
||||||
typedef SEACAVE::cList<Face,const Face&,0,8192,FIndex> FaceArr;
|
typedef SEACAVE::cList<Face,const Face&,0,8192,FIndex> FaceArr;
|
||||||
|
|
||||||
typedef Color8U Color;
|
|
||||||
typedef SEACAVE::cList<Color,const Color&,0,8192,VIndex> ColorArr;
|
|
||||||
|
|
||||||
typedef SEACAVE::cList<VIndex,VIndex,0,8,VIndex> VertexIdxArr;
|
typedef SEACAVE::cList<VIndex,VIndex,0,8,VIndex> VertexIdxArr;
|
||||||
typedef SEACAVE::cList<FIndex,FIndex,0,8,FIndex> FaceIdxArr;
|
typedef SEACAVE::cList<FIndex,FIndex,0,8,FIndex> FaceIdxArr;
|
||||||
typedef SEACAVE::cList<VertexIdxArr,const VertexIdxArr&,2,8192,VIndex> VertexVerticesArr;
|
typedef SEACAVE::cList<VertexIdxArr,const VertexIdxArr&,2,8192,VIndex> VertexVerticesArr;
|
||||||
@@ -157,9 +154,6 @@ public:
|
|||||||
VertexFacesArr vertexFaces; // for each vertex, the ordered list of faces containing it (optional)
|
VertexFacesArr vertexFaces; // for each vertex, the ordered list of faces containing it (optional)
|
||||||
BoolArr vertexBoundary; // for each vertex, stores if it is at the boundary or not (optional)
|
BoolArr vertexBoundary; // for each vertex, stores if it is at the boundary or not (optional)
|
||||||
|
|
||||||
ColorArr vertexColors; // for each vertex, the normal to the surface in that point (optional)
|
|
||||||
ColorArr faceColors; // for each face, the color to it
|
|
||||||
|
|
||||||
NormalArr faceNormals; // for each face, the normal to it (optional)
|
NormalArr faceNormals; // for each face, the normal to it (optional)
|
||||||
FaceFacesArr faceFaces; // for each face, the list of adjacent faces, NO_ID for border edges (optional)
|
FaceFacesArr faceFaces; // for each face, the list of adjacent faces, NO_ID for border edges (optional)
|
||||||
TexCoordArr faceTexcoords; // for each face, the texture-coordinates corresponding to its vertices, 3x num faces OR for each vertex (optional)
|
TexCoordArr faceTexcoords; // for each face, the texture-coordinates corresponding to its vertices, 3x num faces OR for each vertex (optional)
|
||||||
@@ -200,8 +194,6 @@ public:
|
|||||||
void ComputeNormalFaces();
|
void ComputeNormalFaces();
|
||||||
void ComputeNormalVertices();
|
void ComputeNormalVertices();
|
||||||
|
|
||||||
void ComputeColorFaces();
|
|
||||||
|
|
||||||
void SmoothNormalFaces(float fMaxGradient=25.f, float fOriginalWeight=0.5f, unsigned nIterations=3);
|
void SmoothNormalFaces(float fMaxGradient=25.f, float fOriginalWeight=0.5f, unsigned nIterations=3);
|
||||||
|
|
||||||
void GetEdgeFaces(VIndex, VIndex, FaceIdxArr&) const;
|
void GetEdgeFaces(VIndex, VIndex, FaceIdxArr&) const;
|
||||||
@@ -246,10 +238,6 @@ public:
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Color FaceColor(const Face& f) const {
|
|
||||||
return ComputeTriangleColor(vertexColors[f[0]], vertexColors[f[1]], vertexColors[f[2]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Planef EstimateGroundPlane(const ImageArr& images, float sampleMesh=0, float planeThreshold=0, const String& fileExportPlane="") const;
|
Planef EstimateGroundPlane(const ImageArr& images, float sampleMesh=0, float planeThreshold=0, const String& fileExportPlane="") const;
|
||||||
|
|
||||||
Vertex ComputeCentroid(FIndex) const;
|
Vertex ComputeCentroid(FIndex) const;
|
||||||
@@ -274,7 +262,7 @@ public:
|
|||||||
bool TransferTexture(Mesh& mesh, const FaceIdxArr& faceSubsetIndices={}, unsigned borderSize=3, unsigned textureSize=4096);
|
bool TransferTexture(Mesh& mesh, const FaceIdxArr& faceSubsetIndices={}, unsigned borderSize=3, unsigned textureSize=4096);
|
||||||
|
|
||||||
// file IO
|
// file IO
|
||||||
bool Load(const String& fileName, bool *pBExistVertexColor=nullptr);
|
bool Load(const String& fileName);
|
||||||
bool Save(const String& fileName, const cList<String>& comments=cList<String>(), bool bBinary=true) const;
|
bool Save(const String& fileName, const cList<String>& comments=cList<String>(), bool bBinary=true) const;
|
||||||
bool Save(const FacesChunkArr&, const String& fileName, const cList<String>& comments=cList<String>(), bool bBinary=true) const;
|
bool Save(const FacesChunkArr&, const String& fileName, const cList<String>& comments=cList<String>(), bool bBinary=true) const;
|
||||||
static bool Save(const VertexArr& vertices, const String& fileName, bool bBinary=true);
|
static bool Save(const VertexArr& vertices, const String& fileName, bool bBinary=true);
|
||||||
@@ -284,7 +272,7 @@ public:
|
|||||||
static inline VIndex& GetVertex(Face& f, VIndex v) { const uint32_t idx(FindVertex(f, v)); ASSERT(idx != NO_ID); return f[idx]; }
|
static inline VIndex& GetVertex(Face& f, VIndex v) { const uint32_t idx(FindVertex(f, v)); ASSERT(idx != NO_ID); return f[idx]; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool LoadPLY(const String& fileName, bool *pBExistVertexColor);
|
bool LoadPLY(const String& fileName);
|
||||||
bool LoadOBJ(const String& fileName);
|
bool LoadOBJ(const String& fileName);
|
||||||
bool LoadGLTF(const String& fileName, bool bBinary=true);
|
bool LoadGLTF(const String& fileName, bool bBinary=true);
|
||||||
|
|
||||||
|
|||||||
+4
-5
@@ -65,10 +65,8 @@ public:
|
|||||||
std::map<std::string, std::unordered_set<int>> visible_faces_map;
|
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>> edge_faces_map;
|
||||||
std::map<std::string, std::unordered_set<int>> delete_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;
|
std::unordered_set<int> face_visible_relative;
|
||||||
std::unordered_set<int> face_test;
|
|
||||||
|
|
||||||
std::string base_path;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline Scene(unsigned _nMaxThreads=0)
|
inline Scene(unsigned _nMaxThreads=0)
|
||||||
@@ -166,20 +164,21 @@ public:
|
|||||||
std::unordered_set<int>& face_visible_relative,
|
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>>& edge_faces_map,
|
||||||
std::map<std::string, std::unordered_set<int>>& delete_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);
|
std::string& basePath);
|
||||||
bool LoadTestFacesData(std::unordered_set<int>& face_test, std::string& basePath);
|
|
||||||
|
|
||||||
// Mesh texturing
|
// Mesh texturing
|
||||||
bool TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsigned minCommonCameras=0, float fOutlierThreshold=0.f, float fRatioDataSmoothness=0.3f,
|
bool TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsigned minCommonCameras=0, float fOutlierThreshold=0.f, float fRatioDataSmoothness=0.3f,
|
||||||
bool bGlobalSeamLeveling=true, bool bLocalSeamLeveling=true, unsigned nTextureSizeMultiple=0, unsigned nRectPackingHeuristic=3, Pixel8U colEmpty=Pixel8U(255,127,39),
|
bool bGlobalSeamLeveling=true, bool bLocalSeamLeveling=true, unsigned nTextureSizeMultiple=0, unsigned nRectPackingHeuristic=3, Pixel8U colEmpty=Pixel8U(255,127,39),
|
||||||
float fSharpnessWeight=0.5f, int ignoreMaskLabel=-1, int maxTextureSize=0, const IIndexArr& views=IIndexArr(), const SEACAVE::String& basename = "", bool bOriginFaceview = false,
|
float fSharpnessWeight=0.5f, int ignoreMaskLabel=-1, int maxTextureSize=0, const IIndexArr& views=IIndexArr(), const SEACAVE::String& basename = "", bool bOriginFaceview = false,
|
||||||
const std::string& inputFileName = "", const std::string& meshFileName = "", bool bExistVertexColor = false);
|
const std::string& inputFileName = "", const std::string& meshFileName = "");
|
||||||
|
|
||||||
std::string runPython(const std::string& command);
|
std::string runPython(const std::string& command);
|
||||||
bool is_face_visible(const std::string& image_name, int face_index);
|
bool is_face_visible(const std::string& image_name, int face_index);
|
||||||
bool is_face_visible_relative(int face_index);
|
bool is_face_visible_relative(int face_index);
|
||||||
bool is_face_edge(const std::string& image_name, 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_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);
|
void SegmentMeshBasedOnCurvature(Mesh::FaceIdxArr& regionMap, float curvatureThreshold);
|
||||||
|
|
||||||
|
|||||||
+502
-1189
File diff suppressed because it is too large.
Load diff
+99
-153
@@ -1352,14 +1352,88 @@ class ModelProcessor:
|
|||||||
|
|
||||||
face_visible = v0_visible | v1_visible | v2_visible
|
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版本相同的后续处理
|
# 使用与CPU版本相同的后续处理
|
||||||
shrunk_visibility = self._shrink_face_visibility(face_visible.cpu().numpy(), 6)
|
shrunk_visibility = self._shrink_face_visibility(face_visible.cpu().numpy(), 12)
|
||||||
expanded_visibility = self._expand_face_visibility(face_visible.cpu().numpy(), 30)
|
expanded_visibility = self._expand_face_visibility(face_visible.cpu().numpy(), 0)
|
||||||
shrunk_visibility2 = self._shrink_face_visibility(face_visible.cpu().numpy(), 50)
|
shrunk_visibility2 = self._shrink_face_visibility(face_visible.cpu().numpy(), 0)
|
||||||
expanded_edge = expanded_visibility & ~shrunk_visibility2
|
expanded_edge = expanded_visibility & ~shrunk_visibility2
|
||||||
delete_edge = face_visible.cpu().numpy() & ~shrunk_visibility
|
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):
|
def _gen_depth_image_gpu(self, cam_data, render):
|
||||||
@@ -1390,6 +1464,7 @@ class ModelProcessor:
|
|||||||
visible_faces_dict = {}
|
visible_faces_dict = {}
|
||||||
edge_faces_dict = {}
|
edge_faces_dict = {}
|
||||||
delete_edge_faces_dict = {}
|
delete_edge_faces_dict = {}
|
||||||
|
face_normal_visible_dict = {}
|
||||||
|
|
||||||
total_start = time.time()
|
total_start = time.time()
|
||||||
|
|
||||||
@@ -1415,13 +1490,14 @@ class ModelProcessor:
|
|||||||
# continue
|
# continue
|
||||||
|
|
||||||
start_time = time.time()
|
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
|
processing_time = time.time() - start_time
|
||||||
|
|
||||||
visible_faces = np.where(face_visibility)[0].tolist()
|
visible_faces = np.where(face_visibility)[0].tolist()
|
||||||
visible_faces_dict[img_name] = visible_faces
|
visible_faces_dict[img_name] = visible_faces
|
||||||
edge_faces_dict[img_name] = np.where(face_edge)[0].tolist()
|
edge_faces_dict[img_name] = np.where(face_edge)[0].tolist()
|
||||||
delete_edge_faces_dict[img_name] = np.where(face_delete_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)}")
|
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:.2f}秒")
|
||||||
print(f"平均每张图像耗时: {total_time/len(images):.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 {
|
return {
|
||||||
"result1": visible_faces_dict,
|
"result1": visible_faces_dict,
|
||||||
"result2": edge_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)
|
depth = render.render_to_depth_image(z_in_view_space=True)
|
||||||
return np.asarray(depth)
|
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):
|
def _flag_contour(self, camera_data, face_points):
|
||||||
"""标记可见顶点"""
|
"""标记可见顶点"""
|
||||||
vertex_visible = []
|
vertex_visible = []
|
||||||
@@ -2075,97 +2094,10 @@ class ModelProcessor:
|
|||||||
face_visible_bitmap = np.ones(len(triangles), dtype=bool) # 临时填充
|
face_visible_bitmap = np.ones(len(triangles), dtype=bool) # 临时填充
|
||||||
return face_visible_bitmap, face_edge
|
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]],
|
def save_occlusion_data(self, result1: Dict[str, List[int]],
|
||||||
result2: Dict[str, List[int]],
|
result2: Dict[str, List[int]],
|
||||||
result3: Dict[str, List[int]],
|
result3: Dict[str, List[int]],
|
||||||
|
result4: Dict[str, List[int]],
|
||||||
base_path: str) -> None:
|
base_path: str) -> None:
|
||||||
"""
|
"""
|
||||||
保存遮挡数据到文件
|
保存遮挡数据到文件
|
||||||
@@ -2201,6 +2133,9 @@ class ModelProcessor:
|
|||||||
for image_name, face_list in result3.items():
|
for image_name, face_list in result3.items():
|
||||||
delete_edge_faces_map[image_name] = set(face_list)
|
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
|
# 保存 visible_faces_map
|
||||||
try:
|
try:
|
||||||
@@ -2246,6 +2181,17 @@ class ModelProcessor:
|
|||||||
except IOError as e:
|
except IOError as e:
|
||||||
print(f"Error writing delete_edge_faces_map file: {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):
|
def process(self):
|
||||||
|
|
||||||
print("process")
|
print("process")
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
import bpy
|
|
||||||
import os
|
|
||||||
|
|
||||||
mesh = bpy.context.active_object.data
|
|
||||||
|
|
||||||
selected_faces = [polygon.index for polygon in mesh.polygons if polygon.select]
|
|
||||||
|
|
||||||
output_lines = ["详细信息:"]
|
|
||||||
|
|
||||||
for face_idx in selected_faces[:10]: # 只显示前10个
|
|
||||||
face = mesh.polygons[face_idx]
|
|
||||||
output_lines.append(f" 面 {face_idx}: 顶点数={len(face.vertices)}, ={face.area:.4f}")
|
|
||||||
|
|
||||||
text_name = f"selected_faces_report"
|
|
||||||
|
|
||||||
# 删除已存在的同名文本数据块
|
|
||||||
if text_name in bpy.data.texts:
|
|
||||||
bpy.data.texts.remove(bpy.data.texts[text_name])
|
|
||||||
|
|
||||||
text_block = bpy.data.texts.new(text_name)
|
|
||||||
for line in output_lines:
|
|
||||||
text_block.write(line + "\n")
|
|
||||||
|
|
||||||
save_dir = "/home/algo/Documents/openMVS/data/446442/out.446442"
|
|
||||||
os.makedirs(save_dir, exist_ok=True)
|
|
||||||
|
|
||||||
file_path = os.path.join(save_dir, "_test_faces.txt")
|
|
||||||
|
|
||||||
with open(file_path, 'w') as f:
|
|
||||||
for face_index in selected_faces:
|
|
||||||
f.write(f"{face_index}\n")
|
|
||||||
|
|
||||||
print(f"面片索引已保存到: {file_path}")
|
|
||||||
Reference in new issue
Block a user