You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

428 lines
17 KiB

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