diff --git a/apps/TextureMesh/TextureMesh.cpp b/apps/TextureMesh/TextureMesh.cpp index 775d29a..489fb9f 100644 --- a/apps/TextureMesh/TextureMesh.cpp +++ b/apps/TextureMesh/TextureMesh.cpp @@ -1098,7 +1098,9 @@ int main(int argc, LPCTSTR* argv) return EXIT_FAILURE; printf("Now\n"); VERBOSE(MAKE_PATH_SAFE(OPT::strMeshFileName)); - if (!OPT::strMeshFileName.empty() && !scene.mesh.Load(MAKE_PATH_SAFE(OPT::strMeshFileName))) { + bool bVertexColorExists = false; + bool *pBExistVertexColor = &bVertexColorExists; + if (!OPT::strMeshFileName.empty() && !scene.mesh.Load(MAKE_PATH_SAFE(OPT::strMeshFileName), pBExistVertexColor)) { VERBOSE("error: cannot load mesh file"); return EXIT_FAILURE; } @@ -1134,7 +1136,7 @@ int main(int argc, LPCTSTR* argv) 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::fSharpnessWeight, OPT::nIgnoreMaskLabel, OPT::nMaxTextureSize, views, baseFileName, OPT::bOriginFaceview, - OPT::strInputFileName, OPT::strMeshFileName)) + OPT::strInputFileName, OPT::strMeshFileName, *pBExistVertexColor)) 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()); diff --git a/libs/Common/Util.inl b/libs/Common/Util.inl index b0ffa0f..28fda28 100644 --- a/libs/Common/Util.inl +++ b/libs/Common/Util.inl @@ -579,6 +579,16 @@ inline TPoint3 ComputeTriangleNormal(const TPoint3& v0, const TPoint } // ComputeTriangleNormal /*----------------------------------------------------------------*/ +template +ColorType ComputeTriangleColor(const ColorType& c0, const ColorType& c1, const ColorType& c2) { + // 手动计算每个通道的平均值 + ColorType result; + result.r = static_cast((static_cast(c0.r) + c1.r + c2.r) / 3 + 0.5f); // 整数运算避免溢出 + result.g = static_cast((c0.g + c1.g + c2.g) / 3 + 0.5f); + result.b = static_cast((c0.b + c1.b + c2.b) / 3 + 0.5f); + return result; +} + // compute the area of a triangle using Heron's formula template TYPE ComputeTriangleAreaSqLen(TYPE lena, TYPE lenb, TYPE lenc) { diff --git a/libs/MVS/ColorComparisonFace.cpp b/libs/MVS/ColorComparisonFace.cpp new file mode 100644 index 0000000..482ee2b --- /dev/null +++ b/libs/MVS/ColorComparisonFace.cpp @@ -0,0 +1,428 @@ +#include "ColorComparisonFace.h" +#include "Mesh.h" // 在cpp中包含完整定义 +#include + +#include +#include +#include +#include +#include +#include + +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::addColorInfo(int faceId, + const MeshColor& gaussianColor, + const cv::Mat& imageRegion, + float distance, float threshold, + const std::string& filename) { + ColorInfo info = {faceId, gaussianColor, imageRegion.clone(), distance, threshold, filename}; + faceViewColorMap[faceId][filename].push_back(info); + printf("addColorInfo faceId=%d", 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 ColorComparisonFace::getFaceIds() const { + std::vector 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 imageRegionSize = 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 + imageRegionSize + blockMargin * 3; + int blockHeight = imageRegionSize + 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 - Image 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.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 blockIndex = 0; + int currentRow = 0; + int currentCol = 0; + + // 遍历这个face的所有视图 + for (const auto& viewEntry : viewMap) { + const std::string& viewName = viewEntry.first; + const std::vector& 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 imageX = blockX + blockMargin + colorBlockSize + blockMargin; + int imageY = blockY + blockMargin; + + if (!info.imageRegion.empty()) { + // 调整图像区域大小 + cv::Mat resizedRegion; + cv::resize(info.imageRegion, resizedRegion, cv::Size(imageRegionSize, imageRegionSize)); + + // 将图像区域绘制到指定位置 + resizedRegion.copyTo(faceImage(cv::Rect(imageX, imageY, imageRegionSize, imageRegionSize))); + + cv::rectangle(faceImage, + cv::Rect(imageX, imageY, imageRegionSize, imageRegionSize), + cv::Scalar(0, 0, 0), 2); + + // 添加原始图像标签 + cv::putText(faceImage, "ORIGINAL", + cv::Point(imageX + 10, imageY - 5), + cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0), 1); + } else { + // 如果没有图像区域,绘制占位符 + cv::rectangle(faceImage, + cv::Rect(imageX, imageY, imageRegionSize, imageRegionSize), + cv::Scalar(200, 200, 200), -1); + cv::rectangle(faceImage, + cv::Rect(imageX, imageY, imageRegionSize, imageRegionSize), + cv::Scalar(150, 150, 150), 2); + + cv::putText(faceImage, "NO IMAGE", + cv::Point(imageX + imageRegionSize/2 - 40, imageY + imageRegionSize/2), + cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(100, 100, 100), 1); + } + + // 绘制信息区域 + int infoY = blockY + blockMargin + imageRegionSize; + 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("Gaussian: R=%d, G=%d, B=%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("Distance: %.4f", info.distance), + 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.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(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_image_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_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& 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,%.4f,%.4f,%s\n", + faceId, + viewName.c_str(), + info.gaussianColor[0], info.gaussianColor[1], info.gaussianColor[2], + info.distance, info.threshold, + needsFix ? "YES" : "NO"); + } + } + + fclose(fp); + printf("✅ face %d颜色信息已保存到: %s\n", faceId, filePath.c_str()); + } + + // 保存汇总统计信息 + std::string summaryPath = outputDir + "image_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::max(); + float maxDist = 0.0f; + float sumDist = 0.0f; + + for (const auto& viewEntry : viewMap) { + const std::vector& 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()); + } +} \ No newline at end of file diff --git a/libs/MVS/ColorComparisonFace.h b/libs/MVS/ColorComparisonFace.h new file mode 100644 index 0000000..a29f284 --- /dev/null +++ b/libs/MVS/ColorComparisonFace.h @@ -0,0 +1,70 @@ +#ifndef COLORCOMPARISONBFACE_H +#define COLORCOMPARISONBFACE_H + +#include +#include +#include + + +// 不使用前向声明,直接定义简单的颜色结构 +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 ColorInfo { + int faceId; + MeshColor gaussianColor; + cv::Mat imageRegion; // 存储实际图像区域 + float distance; + float threshold; + std::string filename; + }; + + // 使用嵌套map: faceid -> filename -> ColorInfo列表 + std::map>> faceViewColorMap; + std::string outputDir; + +public: + // 构造函数 + ColorComparisonFace(const std::string& dir); + + // 析构函数 + virtual ~ColorComparisonFace() {} + + // 添加颜色信息(带图像区域) + void addColorInfo(int faceId, + const MeshColor& gaussianColor, + const cv::Mat& imageRegion, + float distance, float threshold, + const std::string& filename); + + // 创建实际图像区域对比图 + void createBatchComparison(int maxBlocksPerRow = 6, int maxFacesPerImage = 20); + + // 保存颜色信息到CSV文件 + void saveColorInfoToFile(); + + // 获取总face数 + int getTotalFaces() const; + + // 获取总记录数 + int getTotalRecords() const; + + // 获取faceid列表 + std::vector getFaceIds() const; +}; + +#endif // COLORCOMPARISONBFACE_H \ No newline at end of file diff --git a/libs/MVS/ColorComparisonPixel.cpp b/libs/MVS/ColorComparisonPixel.cpp new file mode 100644 index 0000000..51e466e --- /dev/null +++ b/libs/MVS/ColorComparisonPixel.cpp @@ -0,0 +1,505 @@ +#include "ColorComparisonPixel.h" +#include "Mesh.h" // 在cpp中包含完整定义 +#include + +#include +#include +#include +#include +#include +#include + +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 ColorComparisonPixel::getFaceIds() const { + std::vector 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> viewRowsInfo; // 存储每个视图需要的行数 + + for (const auto& viewEntry : viewMap) { + const std::string& viewName = viewEntry.first; + const std::vector& 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& 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& 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::max(); + float maxDist = 0.0f; + float sumDist = 0.0f; + + for (const auto& viewEntry : viewMap) { + const std::vector& 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()); + } +} \ No newline at end of file diff --git a/libs/MVS/ColorComparisonPixel.h b/libs/MVS/ColorComparisonPixel.h new file mode 100644 index 0000000..5bfb52b --- /dev/null +++ b/libs/MVS/ColorComparisonPixel.h @@ -0,0 +1,74 @@ +#ifndef COLORCOMPARISONPIXEL_H +#define COLORCOMPARISONPIXEL_H + +#include +#include +#include +#include + +#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>> 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 getFaceIds() const; +}; + +#endif // COLORCOMPARISONPIXEL_H \ No newline at end of file diff --git a/libs/MVS/Mesh.cpp b/libs/MVS/Mesh.cpp index f198262..ff94438 100644 --- a/libs/MVS/Mesh.cpp +++ b/libs/MVS/Mesh.cpp @@ -350,6 +350,16 @@ void Mesh::ComputeNormalFaces() #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 #if 1 // computes the vertex normal as the area weighted face normals average @@ -1096,18 +1106,20 @@ namespace BasicPLY { struct Vertex { Mesh::Vertex v; Mesh::Normal n; + Mesh::Color color; static void InitLoadProps(PLY& ply, int elem_count, - Mesh::VertexArr& vertices, Mesh::NormalArr& vertexNormals) + Mesh::VertexArr& vertices, Mesh::ColorArr& vertexColors) { PLY::PlyElement* elm = ply.find_element(elem_names[0]); const size_t nMaxProps(SizeOfArray(props)); for (size_t p=0; p VertexArr; typedef SEACAVE::cList FaceArr; + + typedef Color8U Color; + typedef SEACAVE::cList ColorArr; typedef SEACAVE::cList VertexIdxArr; typedef SEACAVE::cList FaceIdxArr; @@ -154,6 +157,9 @@ public: 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) + 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) 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) @@ -194,6 +200,8 @@ public: void ComputeNormalFaces(); void ComputeNormalVertices(); + void ComputeColorFaces(); + void SmoothNormalFaces(float fMaxGradient=25.f, float fOriginalWeight=0.5f, unsigned nIterations=3); void GetEdgeFaces(VIndex, VIndex, FaceIdxArr&) const; @@ -238,6 +246,10 @@ public: 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; Vertex ComputeCentroid(FIndex) const; @@ -262,7 +274,7 @@ public: bool TransferTexture(Mesh& mesh, const FaceIdxArr& faceSubsetIndices={}, unsigned borderSize=3, unsigned textureSize=4096); // file IO - bool Load(const String& fileName); + bool Load(const String& fileName, bool *pBExistVertexColor=nullptr); bool Save(const String& fileName, const cList& comments=cList(), bool bBinary=true) const; bool Save(const FacesChunkArr&, const String& fileName, const cList& comments=cList(), bool bBinary=true) const; static bool Save(const VertexArr& vertices, const String& fileName, bool bBinary=true); @@ -272,7 +284,7 @@ public: static inline VIndex& GetVertex(Face& f, VIndex v) { const uint32_t idx(FindVertex(f, v)); ASSERT(idx != NO_ID); return f[idx]; } protected: - bool LoadPLY(const String& fileName); + bool LoadPLY(const String& fileName, bool *pBExistVertexColor); bool LoadOBJ(const String& fileName); bool LoadGLTF(const String& fileName, bool bBinary=true); diff --git a/libs/MVS/Scene.h b/libs/MVS/Scene.h index 2f67a26..d7ec610 100644 --- a/libs/MVS/Scene.h +++ b/libs/MVS/Scene.h @@ -66,6 +66,7 @@ public: std::map> edge_faces_map; std::map> delete_edge_faces_map; std::unordered_set face_visible_relative; + std::unordered_set face_test; public: inline Scene(unsigned _nMaxThreads=0) @@ -164,12 +165,13 @@ public: std::map>& edge_faces_map, std::map>& delete_edge_faces_map, std::string& basePath); + bool LoadTestFacesData(std::unordered_set& face_test, std::string& basePath); // Mesh texturing 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), 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 = ""); + const std::string& inputFileName = "", const std::string& meshFileName = "", bool bExistVertexColor = false); std::string runPython(const std::string& command); bool is_face_visible(const std::string& image_name, int face_index); diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 012cf50..d0efe41 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -43,6 +43,8 @@ #include "cuda/MeshTextureCUDA.h" #include #include +#include "ColorComparisonFace.h" +#include "ColorComparisonPixel.h" namespace fs = std::filesystem; @@ -76,6 +78,8 @@ using namespace MVS; #define TEXOPT_INFERENCE_TRWS 2 #define TEXOPT_INFERENCE TEXOPT_INFERENCE_LBP #define MASK_FACE_OCCLUSION +// #define TEST + // #define USE_CUDA // inference algorithm @@ -590,6 +594,336 @@ public: return distances[distances.size() / 2]; } + static Color computeMedianColor(const std::vector& colors) { + if (colors.empty()) return Color::ZERO; + + // 分别计算RGB通道的中位数 + std::vector rVals, gVals, bVals; + rVals.reserve(colors.size()); + gVals.reserve(colors.size()); + bVals.reserve(colors.size()); + + for (const Color& c : colors) { + rVals.push_back(c.z); + gVals.push_back(c.y); + bVals.push_back(c.x); + } + + auto getMedian = [](std::vector& vals) { + size_t n = vals.size() / 2; + std::nth_element(vals.begin(), vals.begin() + n, vals.end()); + float median = vals[n]; + + if (vals.size() % 2 == 0) { + std::nth_element(vals.begin(), vals.begin() + n - 1, vals.end()); + median = (median + vals[n-1]) * 0.5f; + } + return median; + }; + + std::vector rTemp = rVals, gTemp = gVals, bTemp = bVals; + return Color(getMedian(rTemp), getMedian(gTemp), getMedian(bTemp)); + } + + static float computeColorDistance(const Color& c1, const Color& c2) { + // 欧氏距离 + float dr = c1.z - c2.z; + float dg = c1.y - c2.y; + float db = c1.x - c2.x; + return std::sqrt(dr*dr + dg*dg + db*db); + } + + static Color computeRobustFaceColor(const FaceDataArr& faceDatas, float threshold = 2.0f) { + if (faceDatas.empty()) { + return Color::ZERO; + } + + // 1. 提取所有颜色 + std::vector colors; + colors.reserve(faceDatas.size()); + for (const FaceData& fd : faceDatas) { + colors.push_back(fd.color); + } + + // 2. 分别计算RGB三个通道的中位数 + Color medianColor = computeMedianColor(colors); + + // 3. 计算每个颜色到中位数的距离 + std::vector distances; + distances.reserve(colors.size()); + for (const Color& color : colors) { + distances.push_back(computeColorDistance(color, medianColor)); + } + + // 4. 计算距离的中位数和MAD(绝对中位差) + std::vector sortedDistances = distances; + std::sort(sortedDistances.begin(), sortedDistances.end()); + float medianDistance = sortedDistances[sortedDistances.size() / 2]; + + // 计算绝对偏差 + std::vector absoluteDeviations; + absoluteDeviations.reserve(distances.size()); + for (float d : distances) { + absoluteDeviations.push_back(std::abs(d - medianDistance)); + } + std::sort(absoluteDeviations.begin(), absoluteDeviations.end()); + float mad = absoluteDeviations[absoluteDeviations.size() / 2]; + + // 5. 确定阈值(使用3倍MAD规则) + float distanceThreshold = medianDistance + threshold * mad; + + // 6. 计算筛选后的颜色均值 + Color sumColor = Color::ZERO; + int validCount = 0; + + for (size_t i = 0; i < colors.size(); ++i) { + if (distances[i] <= distanceThreshold) { + sumColor += colors[i]; + validCount++; + } else { + // 可选:记录被剔除的颜色 + // std::cout << "剔除异常颜色: " << colors[i] << ", 距离: " << distances[i] << std::endl; + } + } + + if (validCount > 0) { + return sumColor / static_cast(validCount); + } else { + // 如果没有有效颜色,返回中位数 + return medianColor; + } + } + + static Color computeFaceColorDifference(Mesh::Color& refColor, Color& faceColor) { + // printf("refColor:%f,%f,%f; faceColor:%d,%d,%d\n", refColor.r,refColor.g,refColor.b,faceColor[0],faceColor[1],faceColor[2]); + // 将refColor(0-255整数)转换为0-1范围的浮点数 + Color refColorFloat( + refColor.r / 255.0f, + refColor.g / 255.0f, + refColor.b / 255.0f + ); + + // 计算差值(浮点数,可能有负值) + // 假设faceColor已经是0-1范围的浮点数 + Color difference( + refColorFloat[0] - faceColor[0] / 255.0f, + refColorFloat[1] - faceColor[1] / 255.0f, + refColorFloat[2] - faceColor[2] / 255.0f + ); + + return difference; + } + + static float computeFaceColorDifferenceF(Mesh::Color& refColor, Color& faceColor) { + // printf("refColor:%f,%f,%f; faceColor:%d,%d,%d\n", refColor.r,refColor.g,refColor.b,faceColor[0],faceColor[1],faceColor[2]); + // 将refColor(0-255整数)转换为0-1范围的浮点数 + Color refColorFloat( + refColor.r / 255.0f, + refColor.g / 255.0f, + refColor.b / 255.0f + ); + + // 计算差值(浮点数,可能有负值) + // 假设faceColor已经是0-1范围的浮点数 + Color difference( + refColorFloat[0] - faceColor[0] / 255.0f, + refColorFloat[1] - faceColor[1] / 255.0f, + refColorFloat[2] - faceColor[2] / 255.0f + ); + + int dr = difference[0]; + int dg = difference[0]; + int db = difference[0]; + + return std::sqrt(dr*dr + dg*dg + db*db); + + } + + static float computeMeshColorDifferenceF(const Mesh::Color& refColor1, const Mesh::Color& refColor2) { + // 将颜色转换为0-1范围的浮点数 + Color refColorFloat1( + refColor1.r / 255.0f, + refColor1.g / 255.0f, + refColor1.b / 255.0f + ); + + Color refColorFloat2( + refColor2.r / 255.0f, + refColor2.g / 255.0f, + refColor2.b / 255.0f + ); + + // 计算差值(浮点数) + Color difference( + refColorFloat2[0] - refColorFloat1[0], + refColorFloat2[1] - refColorFloat1[1], + refColorFloat2[2] - refColorFloat1[2] + ); + + // 计算欧氏距离(浮点数) + float dr = difference[0]; + float dg = difference[1]; // 修正:使用difference[1] + float db = difference[2]; // 修正:使用difference[2] + + return std::sqrt(dr*dr + dg*dg + db*db); + } + + static float computeColorDifferenceEnhanced(const Mesh::Color& c1, const Mesh::Color& c2) { + // 转换为0-1 + float r1 = c1.r / 255.0f; + float g1 = c1.g / 255.0f; + float b1 = c1.b / 255.0f; + + float r2 = c2.r / 255.0f; + float g2 = c2.g / 255.0f; + float b2 = c2.b / 255.0f; + + // 计算亮度 + float lum1 = 0.299f*r1 + 0.587f*g1 + 0.114f*b1; + float lum2 = 0.299f*r2 + 0.587f*g2 + 0.114f*b2; + + // 亮度差异权重 + float lumDiff = std::abs(lum1 - lum2); + + // 色度差异 + float chromaDiff = std::sqrt( + (r1 - r2)*(r1 - r2) + + (g1 - g2)*(g1 - g2) + + (b1 - b2)*(b1 - b2) + ); + + // 综合差异(亮度权重更高) + return 0.7f * lumDiff + 0.3f * chromaDiff; + } + + static float computeColorDifferenceHSV2(Mesh::Color& c1, Mesh::Color& c2) { + // RGB转HSV + auto rgb2hsv = [](float r, float g, float b, float& h, float& s, float& v) { + float cmax = std::max({r, g, b}); + float cmin = std::min({r, g, b}); + float delta = cmax - cmin; + + v = cmax; + + if (cmax > 0) { + s = delta / cmax; + } else { + s = 0; + } + + if (delta > 0) { + if (cmax == r) { + h = 60.0f * fmod((g - b) / delta, 6.0f); + } else if (cmax == g) { + h = 60.0f * (((b - r) / delta) + 2.0f); + } else if (cmax == b) { + h = 60.0f * (((r - g) / delta) + 4.0f); + } + if (h < 0) h += 360.0f; + } else { + h = 0; + } + }; + + // 转换为0-1 + float r1 = c1.r / 255.0f, g1 = c1.g / 255.0f, b1 = c1.b / 255.0f; + float r2 = c2.r / 255.0f, g2 = c2.g / 255.0f, b2 = c2.b / 255.0f; + + float h1, s1, v1, h2, s2, v2; + rgb2hsv(r1, g1, b1, h1, s1, v1); + rgb2hsv(r2, g2, b2, h2, s2, v2); + + // 归一化H到0-1 + h1 /= 360.0f; h2 /= 360.0f; + + // 计算HSV空间距离(色相差异需考虑环形特性) + float dh = std::min(fabs(h1 - h2), 1.0f - fabs(h1 - h2)); + float ds = fabs(s1 - s2); + float dv = fabs(v1 - v2); + + // 加权距离(色相最重要,饱和度次之,亮度最不重要) + return std::sqrt(0.5f*dh*dh + 0.3f*ds*ds + 0.2f*dv*dv); + } + + static float computeColorDifferenceHSV(MeshColor& c1, MeshColor& c2) { + // RGB转HSV + auto rgb2hsv = [](float r, float g, float b, float& h, float& s, float& v) { + float cmax = std::max({r, g, b}); + float cmin = std::min({r, g, b}); + float delta = cmax - cmin; + + v = cmax; + + if (cmax > 0) { + s = delta / cmax; + } else { + s = 0; + } + + if (delta > 0) { + if (cmax == r) { + h = 60.0f * fmod((g - b) / delta, 6.0f); + } else if (cmax == g) { + h = 60.0f * (((b - r) / delta) + 2.0f); + } else if (cmax == b) { + h = 60.0f * (((r - g) / delta) + 4.0f); + } + if (h < 0) h += 360.0f; + } else { + h = 0; + } + }; + + // 转换为0-1 + float r1 = c1.r / 255.0f, g1 = c1.g / 255.0f, b1 = c1.b / 255.0f; + float r2 = c2.r / 255.0f, g2 = c2.g / 255.0f, b2 = c2.b / 255.0f; + + float h1, s1, v1, h2, s2, v2; + rgb2hsv(r1, g1, b1, h1, s1, v1); + rgb2hsv(r2, g2, b2, h2, s2, v2); + + // 归一化H到0-1 + h1 /= 360.0f; h2 /= 360.0f; + + // 计算HSV空间距离(色相差异需考虑环形特性) + float dh = std::min(fabs(h1 - h2), 1.0f - fabs(h1 - h2)); + float ds = fabs(s1 - s2); + float dv = fabs(v1 - v2); + + // 加权距离(色相最重要,饱和度次之,亮度最不重要) + return std::sqrt(0.5f*dh*dh + 0.3f*ds*ds + 0.2f*dv*dv); + } + + static float computeColorSimilarityWeighted(const Mesh::Color& c1, const Mesh::Color& c2) { + // 人眼对绿色最敏感,红色次之,蓝色最不敏感 + const float weightR = 0.299f; + const float weightG = 0.587f; + const float weightB = 0.114f; + + float dr = (c1.r - c2.r) / 255.0f; + float dg = (c1.g - c2.g) / 255.0f; + float db = (c1.b - c2.b) / 255.0f; + + return std::sqrt(weightR*dr*dr + weightG*dg*dg + weightB*db*db); + } + + static bool isTestFace(std::unordered_set& face_test, int idxFace) { + + if (face_test.find(idxFace) != face_test.end()) { + return true; + } + + // if (idxFace==18919||idxFace==18925||idxFace==18926|| + // idxFace==18932||idxFace==478724||idxFace==803492|| + // idxFace==803496||idxFace==803497||idxFace==803507||idxFace==803508|| + // idxFace==18921) + // { + // return true; + // } + + return false; + } + //*/ /* // 采用ITU-R BT.601标准系数,增加数值稳定性处理 @@ -673,6 +1007,9 @@ public: Mesh::TexIndexArr& faceTexindices; // for each face, the texture-coordinates of the vertices Mesh::Image8U3Arr& texturesDiffuse; // texture containing the diffuse color + Mesh::ColorArr& faceColorsGaussian; + Mesh::ColorArr faceOriginalColors; + // constant the entire time Mesh::VertexArr& vertices; Mesh::FaceArr& faces; @@ -726,14 +1063,23 @@ MeshTexture::MeshTexture(Scene& _scene, unsigned _nResolutionLevel, unsigned _nM faces(_scene.mesh.faces), images(_scene.images), scene(_scene), - alternativeTexture(nullptr) + alternativeTexture(nullptr), + faceColorsGaussian(_scene.mesh.faceColors), + faceOriginalColors() { + // 分配空间 + faceOriginalColors.Resize(_scene.mesh.faceColors.size()); + // 用默认值填充 + for (size_t i = 0; i < faceOriginalColors.size(); ++i) { + faceOriginalColors[i] = Mesh::Color(0, 0, 0); // 或使用其他默认值 + } } MeshTexture::~MeshTexture() { vertexFaces.Release(); vertexBoundary.Release(); faceFaces.Release(); + faceColorsGaussian.Release(); } void MeshTexture::ComputeFaceCurvatures() const { @@ -786,9 +1132,21 @@ void MeshTexture::ListVertexFaces() scene.mesh.ListIncidenteFaceFaces(); } +static ColorComparisonFace* g_colorComparisonFace = nullptr; +static ColorComparisonPixel* g_colorComparisonPixel = nullptr; + // extract array of faces viewed by each image bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThreshold, int nIgnoreMaskLabel, const IIndexArr& _views, bool bUseVirtualFaces) { +#ifdef TEST + std::string outputDir = "/home/algo/Documents/openMVS/data/446442/color_comparison/"; + + g_colorComparisonFace = new ColorComparisonFace(outputDir); + std::map>> testFacePixelsByView; + + g_colorComparisonPixel = new ColorComparisonPixel(outputDir); +#endif + // create faces octree Mesh::Octree octree; Mesh::FacesInserter::CreateOctree(octree, scene.mesh); @@ -1133,6 +1491,100 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr // if (!(scene.mesh.invalidFacesRelative.data.contains(idxFace) && scene.is_face_visible_relative(idxFace))) // if (false) { + /* + Mesh::Color gaussianColor = faceColorsGaussian[idxFace]; + FaceData& faceData = faceDatas.emplace_back(); + faceData.idxView = idxView; + faceData.quality = imageGradMag(j,i); + #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA + + // printf("gaussianColor:%d, %d, %d\n", gaussianColor[0], gaussianColor[1], gaussianColor[2]); + + faceData.color = Color(gaussianColor[0] / 255.0f, gaussianColor[1] / 255.0f, gaussianColor[2] / 255.0f); + + #endif + continue; + //*/ + //* + + if (idxFace threshold) + { + // FaceData& faceData = faceDatas.emplace_back(); + // faceData.idxView = idxView; + // faceData.quality = 0; + // faceData.bInvalidFacesRelative = true; + // printf("faceData.quality=%f\n", faceData.quality); + #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA + // faceData.color = imageData.image(j,i); + #endif + // continue; + } + //*/ +#ifdef TEST + bool bFilter = (colorDistance > threshold) ? true : false; + + std::string full_path = imageData.name; + + // 找到最后一个斜杠 + size_t slash_pos = full_path.find_last_of("/\\"); + + // 提取文件名(带扩展名) + std::string filename = (slash_pos != std::string::npos) + ? full_path.substr(slash_pos + 1) + : full_path; + + // 去掉扩展名 + size_t dot_pos = filename.find_last_of("."); + if (dot_pos != std::string::npos) { + filename = filename.substr(0, dot_pos); + } + + if (isTestFace(scene.face_test, idxFace)) + { + // printf("Test face(%d) Color: gaussian(%d,%d,%d), image(%s) color(%d,%d,%d), colorDistance(%f), threshold(%f), filter=%b\n",idxFace, gaussianColor.r, gaussianColor.g, gaussianColor.b, + // filename.c_str(), originalColor.r, originalColor.g, originalColor.b, colorDistance, threshold, bFilter); + + // 创建MeshColor对象 + MeshColor gaussianMeshColor(gaussianColor.r, gaussianColor.g, gaussianColor.b); + MeshColor originalMeshColor(originalColor.r, originalColor.g, originalColor.b); + // 添加到批处理器 + if (g_colorComparisonPixel) { + g_colorComparisonPixel->addColorInfo(idxFace, + gaussianMeshColor, + originalMeshColor, + colorDistance, threshold, filename); + + } + + // 添加到批处理器 + if (g_colorComparisonFace) { + testFacePixelsByView[idxView][idxFace].push_back(cv::Point(i, j)); + + printf("testFacePixels imageView(%s), idxFace(%d) push_back (%d, %d)\n", filename.c_str(), idxFace, i, j); + } + + } +#endif + } + if (depthMap(j,i)>999.0f) { /* @@ -1145,10 +1597,12 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr // printf("faceData.quality=%f\n", faceData.quality); #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA faceData.color = imageData.image(j,i); + // faceData.color = Point3f::ZERO; #endif continue; - */ + //*/ + /* #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA uint32_t& area = areas[idxFace]; if (area++ == 0) { @@ -1175,6 +1629,7 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr faceData.bInvalidFacesRelative = true; } continue; + //*/ } } @@ -1210,6 +1665,160 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr } } } + +#ifdef TEST + auto itView = testFacePixelsByView.find(idxView); + if (itView != testFacePixelsByView.end()) { + const auto& facePixelsMap = itView->second; + + // 打印视图信息 + printf("=== 处理视图 %d: %s ===\n", idxView, strName.c_str()); + printf("这个视图有 %zu 个测试面\n", facePixelsMap.size()); + + // 遍历这个视图中的所有测试面 + for (const auto& faceEntry : facePixelsMap) { + FIndex idxFace = faceEntry.first; + const std::vector& pixels = faceEntry.second; + + printf(" 处理面 %d: 有 %zu 个像素\n", idxFace, pixels.size()); + + // 检查像素是否为空 + if (pixels.empty()) { + printf(" ⚠️ 面 %d 没有像素,跳过\n", idxFace); + continue; + } + + // 计算面片区域的包围盒 + int minX = INT_MAX, minY = INT_MAX; + int maxX = 0, maxY = 0; + + // 打印前几个像素的位置 + int printLimit = std::min(3, (int)pixels.size()); + for (int i = 0; i < printLimit; ++i) { + printf(" 像素 %d: (%d, %d)\n", i, pixels[i].x, pixels[i].y); + } + if (pixels.size() > 3) { + printf(" 还有 %zu 个像素...\n", pixels.size() - 3); + } + + // 计算包围盒 + for (const auto& pt : pixels) { + if (pt.x < minX) minX = pt.x; + if (pt.x > maxX) maxX = pt.x; + if (pt.y < minY) minY = pt.y; + if (pt.y > maxY) maxY = pt.y; + } + + printf(" 包围盒: minX=%d, maxX=%d, minY=%d, maxY=%d\n", minX, maxX, minY, maxY); + + // 扩展一些边界 + int padding = 2; + int imageWidth = static_cast(imageData.width); + int imageHeight = static_cast(imageData.height); + + // 扩展前检查边界 + minX = std::max(0, minX - padding); + maxX = std::min(imageWidth - 1, maxX + padding); + minY = std::max(0, minY - padding); + maxY = std::min(imageHeight - 1, maxY + padding); + + // 确保扩展后 min <= max + if (minX > maxX) { + printf(" ⚠️ 扩展后 minX > maxX,交换\n"); + std::swap(minX, maxX); + } + if (minY > maxY) { + printf(" ⚠️ 扩展后 minY > maxY,交换\n"); + std::swap(minY, maxY); + } + + int width = maxX - minX + 1; + int height = maxY - minY + 1; + + printf(" 扩展后: 左上(%d,%d) 宽高(%d,%d)\n", minX, minY, width, height); + + // 检查尺寸是否有效 + if (width <= 0 || height <= 0) { + printf(" ⚠️ 面 %d 无效尺寸,跳过\n", idxFace); + continue; + } + + // 检查是否在图像范围内 + if (minX < 0 || minX >= imageWidth || + maxX < 0 || maxX >= imageWidth || + minY < 0 || minY >= imageHeight || + maxY < 0 || maxY >= imageHeight) { + printf(" ⚠️ 面 %d 超出图像范围,跳过\n", idxFace); + continue; + } + + // 提取图像区域 + cv::Rect roi(minX, minY, width, height); + printf(" 提取ROI: x=%d, y=%d, width=%d, height=%d\n", + roi.x, roi.y, roi.width, roi.height); + + // 验证ROI + if (roi.x < 0 || roi.y < 0 || + roi.x + roi.width > imageWidth || + roi.y + roi.height > imageHeight) { + printf(" ⚠️ 面 %d ROI验证失败,跳过\n", idxFace); + continue; + } + + cv::Mat imageRegion = imageData.image(roi).clone(); + + if (imageRegion.empty()) { + printf(" ⚠️ 面 %d 图像区域为空,跳过\n", idxFace); + continue; + } + + printf(" 提取图像区域成功: %dx%d\n", imageRegion.cols, imageRegion.rows); + + // 计算图像区域的平均颜色 + cv::Scalar meanColor = cv::mean(imageRegion); + MeshColor originalColor(meanColor[2], meanColor[1], meanColor[0]); // OpenCV是BGR格式 + + // 获取高斯颜色 + Mesh::Color gaussianColor = faceColorsGaussian[idxFace]; + MeshColor gaussianMeshColor(gaussianColor.r, gaussianColor.g, gaussianColor.b); + + // 计算颜色距离 + float colorDistance = computeColorDifferenceHSV(gaussianMeshColor, originalColor); + + float threshold = 0.31f; // 基础阈值 + bool bFilter = (colorDistance > threshold); + + std::string full_path = imageData.name; + size_t slash_pos = full_path.find_last_of("/\\"); + std::string filename = (slash_pos != std::string::npos) + ? full_path.substr(slash_pos + 1) + : full_path; + size_t dot_pos = filename.find_last_of("."); + if (dot_pos != std::string::npos) { + filename = filename.substr(0, dot_pos); + } + + printf("Test face(%d) Color: gaussian(%d,%d,%d), image(%s) color(%d,%d,%d), colorDistance(%.4f), threshold(%.4f), filter=%s\n", + idxFace, gaussianColor.r, gaussianColor.g, gaussianColor.b, + filename.c_str(), originalColor.r, originalColor.g, originalColor.b, + colorDistance, threshold, bFilter ? "true" : "false"); + + // 添加到批处理器 + if (g_colorComparisonFace) { + g_colorComparisonFace->addColorInfo(idxFace, + gaussianMeshColor, + imageRegion, + colorDistance, threshold, filename); + printf(" ✅ 面 %d 已添加到批处理器\n", idxFace); + } + } + + printf("=== 视图 %d 处理完成 ===\n\n", idxView); + } else { + printf("视图 %d: 没有测试面\n", idxView); + } +#endif + // 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) { @@ -1249,6 +1858,25 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr FaceOutlierDetection(faceDatas, fOutlierThreshold); } #endif + +#ifdef TEST + if (g_colorComparisonFace) { + // 创建实际图像区域对比图 + printf("创建实际图像区域对比图,包含 %d 个face,%d 条记录\n", + g_colorComparisonFace->getTotalFaces(), g_colorComparisonFace->getTotalRecords()); + + g_colorComparisonFace->createBatchComparison(10, 500); + delete g_colorComparisonFace; + g_colorComparisonFace = nullptr; + } + + if (g_colorComparisonPixel) { + g_colorComparisonPixel->createBatchComparison(10, 500); // 每行4个,最多20个 + delete g_colorComparisonPixel; + g_colorComparisonPixel = nullptr; + } +#endif + return true; } @@ -1690,7 +2318,7 @@ std::unordered_set MeshTexture::PerformLocalDepthConsistencyCheck(DepthM // 第三遍:清除不一致区域的深度值(现在可以安全修改原始depthMap) for (int r = 0; r < depthMap.rows; ++r) { for (int c = 0; c < depthMap.cols; ++c) { - if (consistencyMask(r, c) == 255) + if (consistencyMask(r, c) == 255) { const FIndex idxFace = faceMap(r, c); if (idxFace == NO_ID || idxFace >= mesh.faces.size()) { @@ -4215,9 +4843,12 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView for (const FaceData& fd : faceDatas) { sumColor += fd.color; } - faceColors[idxFace] = sumColor / faceDatas.size(); + // faceColors[idxFace] = sumColor / faceDatas.size(); + + faceColors[idxFace] = computeRobustFaceColor(faceDatas, 10.0f); } + do { const FIndex startPos = RAND() % remainingFaces.size(); const FIndex virtualFaceCenterFaceID = remainingFaces[startPos]; @@ -4359,8 +4990,8 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView if (angleDeg <= 45.0f) { - // filteredCams.push_back(idxView); - //* + filteredCams.push_back(idxView); + /* float brightnessScore = CalculateBrightnessScore(imageData); // 亮度评分函数 float angleScore = 1.0f - (angleDeg / 45.0f); float qualityScore = 0.0f; @@ -4382,9 +5013,9 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView } else { - // filteredCams.push_back(idxView); + filteredCams.push_back(idxView); - //* + /* float brightnessScore = CalculateBrightnessScore(imageData); // 亮度评分函数 float angleScore = 1.0f - (angleDeg / 45.0f); float qualityScore = 0.0f; @@ -9234,11 +9865,41 @@ void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLevel for (const FIndex idxFace: texturePatch.faces) { const Face& face = faces[idxFace]; TexCoord* texcoords = faceTexcoords.data()+idxFace*3; + + TexCoord centroid(0,0); + for (int i=0; i<3; ++i) { texcoords[i] = imageData.camera.ProjectPointP(vertices[face[i]]); + centroid += texcoords[i]; ASSERT(imageData.image.isInsideWithBorder(texcoords[i], border)); aabb.InsertFull(texcoords[i]); } + + centroid /= 3.0f; + + // 获取重心处的颜色 + int cx = static_cast(centroid.x + 0.5f); + int cy = static_cast(centroid.y + 0.5f); + + + // 边界检查 + cx = std::max(0, std::min(cx, imageData.image.cols-1)); + cy = std::max(0, std::min(cy, imageData.image.rows-1)); +#ifdef TEST + // 获取颜色 + const Pixel8U& pixel = imageData.image(cy, cx); + faceOriginalColors[idxFace] = Mesh::Color(pixel.r, pixel.g, pixel.b); + + // if (idxFace==18919||idxFace==18925||idxFace==18926|| + // idxFace==18932||idxFace==478724||idxFace==803492|| + // idxFace==803496||idxFace==803497||idxFace==803507||idxFace==803508|| + // idxFace==18921) + if (isTestFace(scene.face_test, idxFace)) + { + Mesh::Color originalColor= faceOriginalColors[idxFace]; + printf("idxFace(%d) record originalColor:%d, %d, %d\n", idxFace, originalColor[0], originalColor[1], originalColor[2]); + } +#endif } // compute relative texture coordinates ASSERT(imageData.image.isInside(Point2f(aabb.ptMin))); @@ -9464,7 +10125,78 @@ void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLevel patch = patch.t(); x = 1; y = 0; } - + + // 检查并处理目标面 + for (const FIndex idxFace: texturePatch.faces) { + if (isTestFace(scene.face_test, idxFace)) + // if (idxFace==78133) + { + // 找到这个面对应于纹理块中的区域 + TexCoord* texcoords = faceTexcoords.data() + idxFace * 3; + + // 计算在纹理块内的局部坐标 + TexCoord localCoords[3]; + for (int v = 0; v < 3; ++v) { + localCoords[v] = TexCoord( + texcoords[v][x], + texcoords[v][y] + ); + } + + // 转换为相对于纹理块的坐标 + TexCoord offset(texturePatch.rect.tl()); + for (int v = 0; v < 3; ++v) { + localCoords[v] += offset; + } + + // 在patch中填充三角形为黑色 + cv::Point2f triangle[3]; + for (int v = 0; v < 3; ++v) { + triangle[v] = cv::Point2f( + localCoords[v].x - texturePatch.rect.x, + localCoords[v].y - texturePatch.rect.y + ); + } + + const cv::Point2f* ppt[1] = { triangle }; + int npt[] = { 3 }; + std::vector trianglePoints(3); + for (int v = 0; v < 3; ++v) { + trianglePoints[v] = cv::Point( + static_cast(localCoords[v].x - texturePatch.rect.x + 0.5f), + static_cast(localCoords[v].y - texturePatch.rect.y + 0.5f) + ); + } + std::vector> contours = {trianglePoints}; + // cv::fillPoly(patch, contours, cv::Scalar(0, 0, 0)); + + Mesh::Color gaussianColor = faceColorsGaussian[idxFace]; + // printf("idxFace(%d) gaussianColor:%d, %d, %d\n", idxFace, gaussianColor[0], gaussianColor[1], gaussianColor[2]); + Mesh::Color originalColor= faceOriginalColors[idxFace]; + // printf("idxFace(%d) originalColor:%d, %d, %d\n", idxFace, originalColor[0], originalColor[1], originalColor[2]); + + MeshColor gaussianMeshColor(gaussianColor.r, gaussianColor.g, gaussianColor.b); + MeshColor originalMeshColor(originalColor.r, originalColor.g, originalColor.b); + + // float colorDistance = computeColorSimilarityWeighted(gaussianColor, originalColor); + // float colorDistance = computeColorDifferenceEnhanced(gaussianColor, originalColor); + float colorDistance = computeColorDifferenceHSV(gaussianMeshColor, originalMeshColor); + + // printf("colorDistance=%f\n",colorDistance); + + float threshold = 0.12f; // 基础阈值 + + // 考虑面的面积:小面对颜色变化更敏感 + // float faceArea = faces[idxFace].area; + // if (faceArea < 0.001f) { // 非常小的面 + // threshold *= 0.8f; // 更严格的阈值 + // } + + // if (colorDistance>threshold) + // cv::fillPoly(patch, contours, cv::Scalar(gaussianColor[0], gaussianColor[1], gaussianColor[2])); + } + } + patch.copyTo(texturesDiffuse[idxTexture](rect)); } else @@ -10406,6 +11138,26 @@ bool Scene::LoadVisibleFacesData(std::map>& return true; } +// 从文件加载遮挡数据 +bool Scene::LoadTestFacesData(std::unordered_set& face_test, std::string& basePath) { + printf("LoadTestFacesData %s\n", basePath.c_str()); + + std::ifstream testFacesFile(basePath + "_test_faces.txt"); + if (!testFacesFile.is_open()) { + return false; + } + + std::string line; + while (std::getline(testFacesFile, line)) { + int face_index = std::stoi(line); + face_test.insert(face_index); + printf("insert face_index=%d\n", face_index); + } + testFacesFile.close(); + + return true; +} + // texture mesh // - minCommonCameras: generate texture patches using virtual faces composed of coplanar triangles sharing at least this number of views (0 - disabled, 3 - good value) // - fSharpnessWeight: sharpness weight to be applied on the texture (0 - disabled, 0.5 - good value) @@ -10413,7 +11165,7 @@ bool Scene::LoadVisibleFacesData(std::map>& bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsigned minCommonCameras, float fOutlierThreshold, float fRatioDataSmoothness, bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int nIgnoreMaskLabel, int maxTextureSize, const IIndexArr& views, const SEACAVE::String& baseFileName, bool bOriginFaceview, - const std::string& inputFileName, const std::string& meshFileName) + const std::string& inputFileName, const std::string& meshFileName, bool bExistVertexColor) { if (!bOriginFaceview) { @@ -10427,6 +11179,14 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi if (mesh.faceNormals.empty()) { mesh.ComputeNormalFaces(); // 计算面法向量 } + + if (bExistVertexColor) + { + printf("bExistVertexColor true\n"); + if (mesh.faceColors.empty()) { + mesh.ComputeColorFaces(); // 计算面法向量 + } + } // 确保顶点边界信息已计算 if (mesh.vertexBoundary.empty()) { @@ -10481,7 +11241,6 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi } printf("id=%s\n", id.c_str()); -#ifdef MASK_FACE_OCCLUSION //* fs::path p(baseFileName.c_str()); // 2. 获取父路径 (e.g., /path/to/data/scene) @@ -10499,6 +11258,7 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi basePath = baseFileName.substr(0, secondLastSlash + 1); /*/ +#ifdef MASK_FACE_OCCLUSION // printf("basePath=%s\n", basePath.c_str()); if (!LoadVisibleFacesData(visible_faces_map, face_visible_relative, edge_faces_map, delete_edge_faces_map, basePath)) @@ -10507,6 +11267,13 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi } #endif +#ifdef TEST + if (!LoadTestFacesData(face_test, basePath)) + { + printf("LoadVisibleFacesData error\n"); + } +#endif + // assign the best view to each face { TD_TIMER_STARTD();