From de240806060c07a71b407675096bd3526bd3bf50 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Thu, 12 Feb 2026 11:40:24 +0800 Subject: [PATCH] =?UTF-8?q?=E8=89=B2=E5=9D=97=E6=AF=94=E8=BE=83=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/ColorComparisonFace.cpp | 127 +++++----- libs/MVS/ColorComparisonFace.h | 43 ++-- libs/MVS/SceneTexture.cpp | 417 +++++++++++++++++++++++-------- 3 files changed, 410 insertions(+), 177 deletions(-) diff --git a/libs/MVS/ColorComparisonFace.cpp b/libs/MVS/ColorComparisonFace.cpp index ed4b636..2e4fe45 100644 --- a/libs/MVS/ColorComparisonFace.cpp +++ b/libs/MVS/ColorComparisonFace.cpp @@ -66,16 +66,27 @@ ColorComparisonFace::ColorComparisonFace(const std::string& dir) : outputDir(dir } } -// 添加颜色信息(带图像区域) -void ColorComparisonFace::addColorInfo(int faceId, - const MeshColor& gaussianColor, - const MeshColor& originalColor, - const cv::Mat& imageRegion, - float distance, float threshold, - const std::string& filename) { - ColorInfo info = {faceId, gaussianColor, originalColor, imageRegion.clone(), distance, threshold, filename}; +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("addColorInfo faceId=%d", faceId); + printf("addExactTriangleInfo faceId=%d\n", faceId); } // 获取总face数 @@ -103,20 +114,20 @@ std::vector ColorComparisonFace::getFaceIds() const { return faceIds; } -// 创建实际图像区域对比图 +// 创建三角形区域对比图 void ColorComparisonFace::createBatchComparison(int maxBlocksPerRow, int maxFacesPerImage) { if (faceViewColorMap.empty()) { printf("⚠️ 没有颜色信息可生成\n"); return; } - printf("正在创建实际图像区域对比图...\n"); + printf("正在创建三角形区域对比图...\n"); printf("总face数: %zu\n", faceViewColorMap.size()); printf("总记录数: %d\n", getTotalRecords()); // 块参数 - int imageRegionSize = 200; // 图像区域显示大小 - int colorBlockSize = 100; // 高斯颜色块大小 + int triangleSize = 200; // 三角形区域显示大小 + int colorBlockSize = 100; // 颜色块大小 int blockMargin = 20; // 块之间的边距 int infoHeight = 100; // 信息区域高度 @@ -143,8 +154,8 @@ void ColorComparisonFace::createBatchComparison(int maxBlocksPerRow, int maxFace int numRows = (numBlocks + numCols - 1) / numCols; // 计算每个块的宽度和高度 - int blockWidth = colorBlockSize + imageRegionSize + blockMargin * 3; - int blockHeight = imageRegionSize + blockMargin + infoHeight; + int blockWidth = colorBlockSize + triangleSize + blockMargin * 3; + int blockHeight = triangleSize + blockMargin + infoHeight; // 图片总尺寸 int totalWidth = numCols * (blockWidth + blockMargin) + blockMargin; @@ -154,7 +165,7 @@ void ColorComparisonFace::createBatchComparison(int maxBlocksPerRow, int maxFace cv::Mat faceImage(totalHeight, totalWidth, CV_8UC3, cv::Scalar(245, 245, 245)); // 添加标题 - std::string title = cv::format("Face %d - Image Region Comparison", faceId); + 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); @@ -165,7 +176,7 @@ void ColorComparisonFace::createBatchComparison(int maxBlocksPerRow, int maxFace for (const auto& viewEntry : viewMap) { totalRecords += viewEntry.second.size(); for (const auto& info : viewEntry.second) { - if (info.distance > info.threshold) needFixCount++; + if (info.colorDistance > info.threshold) needFixCount++; } } @@ -182,7 +193,7 @@ void ColorComparisonFace::createBatchComparison(int maxBlocksPerRow, int maxFace // 遍历这个face的所有视图 for (const auto& viewEntry : viewMap) { const std::string& viewName = viewEntry.first; - const std::vector& infos = viewEntry.second; + const std::vector& infos = viewEntry.second; for (const auto& info : infos) { // 计算当前块的位置 @@ -214,42 +225,42 @@ void ColorComparisonFace::createBatchComparison(int maxBlocksPerRow, int maxFace 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; + // 绘制三角形区域 + int triangleX = blockX + blockMargin + colorBlockSize + blockMargin; + int triangleY = blockY + blockMargin; - if (!info.imageRegion.empty()) { - // 调整图像区域大小 - cv::Mat resizedRegion; - cv::resize(info.imageRegion, resizedRegion, cv::Size(imageRegionSize, imageRegionSize)); + if (!info.visualization.empty()) { + // 调整三角形区域大小 + cv::Mat resizedTriangle; + cv::resize(info.visualization, resizedTriangle, cv::Size(triangleSize, triangleSize)); - // 将图像区域绘制到指定位置 - resizedRegion.copyTo(faceImage(cv::Rect(imageX, imageY, imageRegionSize, imageRegionSize))); + // 将三角形区域绘制到指定位置(已经是RGB顺序) + resizedTriangle.copyTo(faceImage(cv::Rect(triangleX, triangleY, triangleSize, triangleSize))); cv::rectangle(faceImage, - cv::Rect(imageX, imageY, imageRegionSize, imageRegionSize), + cv::Rect(triangleX, triangleY, triangleSize, triangleSize), 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); + // 添加三角形标签 + 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(imageX, imageY, imageRegionSize, imageRegionSize), + cv::Rect(triangleX, triangleY, triangleSize, triangleSize), cv::Scalar(200, 200, 200), -1); cv::rectangle(faceImage, - cv::Rect(imageX, imageY, imageRegionSize, imageRegionSize), + cv::Rect(triangleX, triangleY, triangleSize, triangleSize), cv::Scalar(150, 150, 150), 2); - cv::putText(faceImage, "NO IMAGE", - cv::Point(imageX + imageRegionSize/2 - 40, imageY + imageRegionSize/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 + imageRegionSize; + int infoY = blockY + blockMargin + triangleSize; cv::rectangle(faceImage, cv::Rect(blockX, infoY, blockWidth, infoHeight), cv::Scalar(240, 240, 240), -1); @@ -286,7 +297,7 @@ void ColorComparisonFace::createBatchComparison(int maxBlocksPerRow, int maxFace // 添加距离和阈值 cv::putText(faceImage, - cv::format("Distance: %.4f", info.distance), + cv::format("Distance: %.4f", info.colorDistance), cv::Point(blockX + 10, infoY + 80), cv::FONT_HERSHEY_SIMPLEX, 0.35, cv::Scalar(0, 0, 0), 1); @@ -299,8 +310,8 @@ void ColorComparisonFace::createBatchComparison(int maxBlocksPerRow, int maxFace 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); + 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), @@ -327,12 +338,12 @@ void ColorComparisonFace::createBatchComparison(int maxBlocksPerRow, int maxFace } // 保存图片 - std::string outputPath = outputDir + cv::format("face_%d_image_comparison.png", faceId); + 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()); + printf("❌❌ 无法保存face %d的三角形对比图: %s\n", faceId, outputPath.c_str()); } else { - printf("✅ face %d图像区域对比图已保存: %s\n", faceId, outputPath.c_str()); + printf("✅ face %d三角形对比图已保存: %s\n", faceId, outputPath.c_str()); printf(" 尺寸: %d x %d 像素, 视图数: %zu, 记录数: %d\n", totalWidth, totalHeight, viewMap.size(), totalRecords); } @@ -355,11 +366,11 @@ void ColorComparisonFace::saveColorInfoToFile() { continue; } - std::string filePath = outputDir + cv::format("face_%d_colors.csv", faceId); + 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()); + printf("❌❌ 无法创建文件: %s\n", filePath.c_str()); continue; } @@ -368,26 +379,26 @@ void ColorComparisonFace::saveColorInfoToFile() { for (const auto& viewEntry : viewMap) { const std::string& viewName = viewEntry.first; - const std::vector& infos = viewEntry.second; + 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); + 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.distance, info.threshold, + info.colorDistance, info.threshold, needsFix ? "YES" : "NO"); } } fclose(fp); - printf("✅ face %d颜色信息已保存到: %s\n", faceId, filePath.c_str()); + printf("✅ face %d三角形颜色信息已保存到: %s\n", faceId, filePath.c_str()); } // 保存汇总统计信息 - std::string summaryPath = outputDir + "image_comparison_summary.csv"; + std::string summaryPath = outputDir + "triangle_comparison_summary.csv"; FILE* summaryFp = fopen(summaryPath.c_str(), "w"); if (summaryFp) { @@ -404,14 +415,14 @@ void ColorComparisonFace::saveColorInfoToFile() { float sumDist = 0.0f; for (const auto& viewEntry : viewMap) { - const std::vector& infos = viewEntry.second; + 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; + if (info.colorDistance > info.threshold) needFixCount++; + minDist = std::min(minDist, info.colorDistance); + maxDist = std::max(maxDist, info.colorDistance); + sumDist += info.colorDistance; } } @@ -430,6 +441,6 @@ void ColorComparisonFace::saveColorInfoToFile() { } fclose(summaryFp); - printf("✅ 汇总统计信息已保存到: %s\n", summaryPath.c_str()); + printf("✅ 三角形汇总统计信息已保存到: %s\n", summaryPath.c_str()); } } \ No newline at end of file diff --git a/libs/MVS/ColorComparisonFace.h b/libs/MVS/ColorComparisonFace.h index 9334dd0..ee38757 100644 --- a/libs/MVS/ColorComparisonFace.h +++ b/libs/MVS/ColorComparisonFace.h @@ -4,7 +4,7 @@ #include #include #include - +#include // 不使用前向声明,直接定义简单的颜色结构 struct MeshColor { @@ -24,18 +24,19 @@ struct MeshColor { class ColorComparisonFace { private: - struct ColorInfo { - int faceId; - MeshColor gaussianColor; - MeshColor originalColor; - cv::Mat imageRegion; // 存储实际图像区域 - float distance; - float threshold; - std::string filename; + struct FaceColorInfo { + int faceId; // 面的ID + MeshColor gaussianColor; // 高斯颜色 + MeshColor originalColor; // 原始颜色 + cv::Mat triangleRegion; // 带透明通道的三角形区域 + cv::Mat visualization; // 三角形可视化图像 + float colorDistance; // 颜色距离 + float threshold; // 阈值 + std::string filename; // 文件名 }; - - // 使用嵌套map: faceid -> filename -> ColorInfo列表 - std::map>> faceViewColorMap; + + // 使用嵌套map: faceid -> filename -> FaceColorInfo列表 + std::map>> faceViewColorMap; std::string outputDir; public: @@ -45,15 +46,17 @@ public: // 析构函数 virtual ~ColorComparisonFace() {} - // 添加颜色信息(带图像区域) - void addColorInfo(int faceId, - const MeshColor& gaussianColor, - const MeshColor& originColor, - const cv::Mat& imageRegion, - float distance, float threshold, - const std::string& filename); + // 添加精确三角形信息(替代原来的addColorInfo) + void addExactTriangleInfo(int faceId, + MeshColor gaussianColor, + MeshColor originalColor, + const cv::Mat& triangleRegionWithAlpha, // 带透明通道 + const cv::Mat& visualization, // 可视化图像 + float colorDistance, + float threshold, + const std::string& filename); - // 创建实际图像区域对比图 + // 创建三角形区域对比图 void createBatchComparison(int maxBlocksPerRow = 6, int maxFacesPerImage = 20); // 保存颜色信息到CSV文件 diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 6526dec..bca5a4c 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -924,6 +924,67 @@ public: return false; } + // 提取文件名(不带路径和扩展名) + static std::string extractFilename(const std::string& fullpath) { + size_t lastSlash = fullpath.find_last_of("/\\"); + if (lastSlash == std::string::npos) lastSlash = 0; + else lastSlash++; + + size_t lastDot = fullpath.find_last_of('.'); + if (lastDot == std::string::npos) lastDot = fullpath.size(); + + return fullpath.substr(lastSlash, lastDot - lastSlash); + } + + // 绘制三角形区域的可视化 + static void drawTriangleRegionVisualization(cv::Mat& image, + const std::vector& trianglePixels, + const cv::Rect& roi, + const std::vector& triangleProjection) { + // 创建原始图像的副本 + cv::Mat visualization = image.clone(); + + // 绘制三角形像素点 + for (const auto& pt : trianglePixels) { + if (pt.x >= 0 && pt.x < image.cols && pt.y >= 0 && pt.y < image.rows) { + cv::circle(visualization, pt, 1, cv::Scalar(0, 0, 255), -1); + } + } + + // 绘制三角形轮廓 + if (triangleProjection.size() >= 3) { + std::vector polyPoints(triangleProjection.size()); + for (size_t i = 0; i < triangleProjection.size(); ++i) { + polyPoints[i] = cv::Point( + static_cast(std::round(triangleProjection[i].x)), + static_cast(std::round(triangleProjection[i].y)) + ); + } + + // 连接三角形顶点 + for (size_t i = 0; i < polyPoints.size(); ++i) { + size_t j = (i + 1) % polyPoints.size(); + cv::line(visualization, polyPoints[i], polyPoints[j], cv::Scalar(0, 255, 0), 2); + } + + // 在顶点处画圆 + for (size_t i = 0; i < polyPoints.size(); ++i) { + cv::circle(visualization, polyPoints[i], 4, cv::Scalar(255, 0, 0), -1); + } + } + + // 绘制ROI矩形 + cv::rectangle(visualization, roi, cv::Scalar(255, 255, 0), 1); + + // 添加文本信息 + std::string text = cv::format("Triangle Pixels: %zu", trianglePixels.size()); + cv::putText(visualization, text, cv::Point(10, 30), + cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(255, 255, 255), 2); + + // 返回可视化结果 + image = visualization; + } + //*/ /* // 采用ITU-R BT.601标准系数,增加数值稳定性处理 @@ -1671,146 +1732,304 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr 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; + const std::vector& trianglePixels = faceEntry.second; - printf(" 处理面 %d: 有 %zu 个像素\n", idxFace, pixels.size()); + printf(" 处理面 %d: 有 %zu 个像素(三角形精确区域)\n", idxFace, trianglePixels.size()); - // 检查像素是否为空 - if (pixels.empty()) { + if (trianglePixels.empty()) { printf(" ⚠️ 面 %d 没有像素,跳过\n", idxFace); continue; } - // 计算面片区域的包围盒 + // 方法2:从faceMap中获取三角形的精确像素 + // 这些像素已经是三角形在图像上的准确投影 + + // 计算三角形区域的边界 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) { + // 计算三角形区域的边界 + for (const auto& pt : trianglePixels) { 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); - } + printf(" 三角形区域边界: (%d,%d) 到 (%d,%d)\n", minX, minY, maxX, 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::Mat exactMask = cv::Mat::zeros(height, width, CV_8UC1); + + // 填充三角形像素 + for (const auto& pt : trianglePixels) { + int x = pt.x - minX; + int y = pt.y - minY; + if (x >= 0 && x < width && y >= 0 && y < height) { + exactMask.at(y, x) = 255; + } } // 提取图像区域 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; - } + // 创建带透明通道的图像 + cv::Mat exactTriangleRegionWithAlpha = cv::Mat::zeros(height, width, CV_8UC4); - printf(" 提取图像区域成功: %dx%d\n", imageRegion.cols, imageRegion.rows); + // 计算三角形区域的平均颜色 + cv::Scalar meanColor(0, 0, 0); + int pixelCount = 0; - // 计算图像区域的平均颜色 - 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); + // 先计算平均颜色 + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + uchar maskVal = exactMask.at(y, x); + if (maskVal > 0) { + cv::Vec3b pixel = imageRegion.at(y, x); + // 注意:OpenCV是BGR顺序 + meanColor[0] += pixel[0]; // B + meanColor[1] += pixel[1]; // G + meanColor[2] += pixel[2]; // R + pixelCount++; + + // 填充带透明通道的图像 + exactTriangleRegionWithAlpha.at(y, x) = + cv::Vec4b(pixel[0], pixel[1], pixel[2], 255); + } else { + // 在三角形区域外:完全透明 + exactTriangleRegionWithAlpha.at(y, x) = + cv::Vec4b(0, 0, 0, 0); + } + } } - 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 (pixelCount > 0) { + meanColor[0] /= pixelCount; // B + meanColor[1] /= pixelCount; // G + meanColor[2] /= pixelCount; // R + } - // 添加到批处理器 - if (g_colorComparisonFace) { - g_colorComparisonFace->addColorInfo(idxFace, - gaussianMeshColor, - originalColor, - imageRegion, - colorDistance, threshold, filename); - printf(" ✅ 面 %d 已添加到批处理器\n", idxFace); + // 获取高斯颜色 + if (idxFace < faceColorsGaussian.size()) { + Mesh::Color gaussianColor = faceColorsGaussian[idxFace]; + MeshColor gaussianMeshColor(gaussianColor.r, gaussianColor.g, gaussianColor.b); + + // 注意:meanColor是BGR顺序,需要转换为RGB + MeshColor originalColor(meanColor[2], meanColor[1], meanColor[0]); + + // 计算颜色距离 + 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("三角形颜色信息 - Face %d:\n", idxFace); + printf(" 高斯颜色: R=%d, G=%d, B=%d\n", gaussianColor.r, gaussianColor.g, gaussianColor.b); + printf(" 原始颜色: B=%.0f, G=%.0f, R=%.0f\n", meanColor[0], meanColor[1], meanColor[2]); + printf(" 原始颜色(RGB): R=%.0f, G=%.0f, B=%.0f\n", meanColor[2], meanColor[1], meanColor[0]); + printf(" 像素数量: %d\n", pixelCount); + printf(" 颜色距离: %.4f, 阈值: %.4f, 过滤: %s\n", + colorDistance, threshold, bFilter ? "是" : "否"); + + // 添加到批处理器 + if (g_colorComparisonFace) { + // 创建可视化图像(转换为RGB用于显示) + cv::Mat visualization = cv::Mat::zeros(height, width, CV_8UC3); + + // 创建RGB版本的图像区域用于显示 + cv::Mat imageRegionRGB; + cv::cvtColor(imageRegion, imageRegionRGB, cv::COLOR_BGR2RGB); + + bool b = false; + if (filename=="106_8") + b = true; + + // 将三角形区域绘制到可视化图像上 + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + uchar maskVal = exactMask.at(y, x); + if (maskVal > 0) { + // 在三角形区域内:使用RGB颜色 + cv::Vec3b pixel = imageRegionRGB.at(y, x); + visualization.at(y, x) = pixel; + } else { + // 在三角形区域外:使用浅灰色背景 + visualization.at(y, x) = cv::Vec3b(200, 200, 200); + } + } + } + + { + // 立即保存visualization用于调试 + std::string debugDir = "debug_visualization/"; + std::filesystem::create_directories(debugDir); + std::string debugPath = debugDir + "face_" + std::to_string(idxFace) + + "_view" + std::to_string(idxView) + "_" + filename + "_raw_visualization.png"; + cv::imwrite(debugPath, visualization); + printf(" ✅ 保存原始visualization: %s\n", debugPath.c_str()); + + // 同时保存掩码和原始图像用于对比 + cv::imwrite(debugDir + "face_" + std::to_string(idxFace) + "_mask.png", exactMask); + cv::imwrite(debugDir + "face_" + std::to_string(idxFace) + "_original.png", imageRegion); + } + + // 增强三角形轮廓可见性 + cv::Mat visualizationWithContour = visualization.clone(); + + // 创建三角形轮廓 + std::vector hullPoints; + if (trianglePixels.size() >= 3) { + // 计算凸包以获得三角形轮廓 + std::vector allPoints = trianglePixels; + + // 调整坐标到ROI + for (auto& pt : allPoints) { + pt.x -= minX; + pt.y -= minY; + } + + // 计算凸包 + std::vector hull; + cv::convexHull(allPoints, hull); + + // 绘制三角形轮廓 - 增强版本 + if (!hull.empty()) { + // 首先绘制一个粗的蓝色轮廓 + std::vector> contours = {hull}; + cv::drawContours(visualizationWithContour, contours, 0, cv::Scalar(255, 0, 0), 3); + + // 再绘制一个细的白色轮廓,增强对比 + cv::drawContours(visualizationWithContour, contours, 0, cv::Scalar(255, 255, 255), 1); + + // 标记顶点 + for (size_t i = 0; i < hull.size(); i++) { + // 绘制大圆点 + cv::circle(visualizationWithContour, hull[i], 6, cv::Scalar(0, 255, 0), -1); + + // 添加顶点编号 + cv::putText(visualizationWithContour, std::to_string(i), + cv::Point(hull[i].x + 8, hull[i].y - 8), + cv::FONT_HERSHEY_SIMPLEX, 0.7, + cv::Scalar(0, 0, 0), 2); + + // 在顶点周围添加白色边框 + cv::circle(visualizationWithContour, hull[i], 6, cv::Scalar(255, 255, 255), 1); + } + + // 计算三角形中心点 + cv::Point center(0, 0); + for (const auto& p : hull) { + center.x += p.x; + center.y += p.y; + } + center.x /= hull.size(); + center.y /= hull.size(); + + // 在中心添加面编号 + std::string faceLabel = "Face:" + std::to_string(idxFace); + cv::putText(visualizationWithContour, faceLabel, + cv::Point(center.x - 30, center.y), + cv::FONT_HERSHEY_SIMPLEX, 0.5, + cv::Scalar(0, 0, 0), 2); + + // 在三角形周围添加边框 + cv::Rect boundingRect = cv::boundingRect(hull); + cv::rectangle(visualizationWithContour, boundingRect, cv::Scalar(0, 0, 255), 2); + } + } + + // 创建增强版本的带透明通道的图像,包含轮廓 + cv::Mat exactTriangleRegionWithContour = exactTriangleRegionWithAlpha.clone(); + + // 在透明图像上也绘制轮廓 + if (trianglePixels.size() >= 3) { + std::vector allPoints = trianglePixels; + for (auto& pt : allPoints) { + pt.x -= minX; + pt.y -= minY; + } + + std::vector hull; + cv::convexHull(allPoints, hull); + + if (!hull.empty()) { + std::vector> contours = {hull}; + + // 在BGRA图像上绘制轮廓 + cv::drawContours(exactTriangleRegionWithContour, contours, 0, + cv::Scalar(255, 0, 0, 255), 3); // 蓝色轮廓,不透明 + } + } + + // 修复3:传递带透明通道的三角形区域 + g_colorComparisonFace->addExactTriangleInfo(idxFace, + gaussianMeshColor, + originalColor, + exactTriangleRegionWithContour, // 传递带轮廓的透明通道图像 + visualization, // 传递带轮廓的可视化图像 + colorDistance, threshold, filename); + + printf(" ✅ 面 %d 已添加到批处理器(增强轮廓可见性)\n", idxFace); + + // 调试:保存带透明通道的图像 + #ifdef DEBUG_SAVE_TRIANGLES + static int saveCount = 0; + std::string saveDir = "debug_triangles/"; + std::filesystem::create_directories(saveDir); + + std::string trianglePath = saveDir + "face_" + std::to_string(idxFace) + + "_view" + std::to_string(idxView) + "_" + filename + ".png"; + std::string maskPath = saveDir + "face_" + std::to_string(idxFace) + + "_view" + std::to_string(idxView) + "_" + filename + "_mask.png"; + std::string visPath = saveDir + "face_" + std::to_string(idxFace) + + "_view" + std::to_string(idxView) + "_" + filename + "_vis.png"; + std::string rgbPath = saveDir + "face_" + std::to_string(idxFace) + + "_view" + std::to_string(idxView) + "_" + filename + "_rgb.png"; + std::string contourPath = saveDir + "face_" + std::to_string(idxFace) + + "_view" + std::to_string(idxView) + "_" + filename + "_contour.png"; + + // 保存带透明通道的三角形区域 + cv::imwrite(trianglePath, exactTriangleRegionWithContour); + + // 保存掩码 + cv::imwrite(maskPath, exactMask); + + // 保存可视化图像 + cv::imwrite(visPath, visualizationWithContour); + + // 保存RGB版本的图像区域 + cv::imwrite(rgbPath, imageRegionRGB); + + printf(" 已保存三角形图像到: %s\n", trianglePath.c_str()); + printf(" 已保存掩码到: %s\n", maskPath.c_str()); + printf(" 已保存可视化到: %s\n", visPath.c_str()); + #endif + } } }