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