#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::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 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 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& 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& 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::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.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()); } }