|
|
|
@ -924,6 +924,67 @@ public: |
|
|
|
return false; |
|
|
|
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<cv::Point>& trianglePixels, |
|
|
|
|
|
|
|
const cv::Rect& roi, |
|
|
|
|
|
|
|
const std::vector<cv::Point2f>& 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<cv::Point> polyPoints(triangleProjection.size()); |
|
|
|
|
|
|
|
for (size_t i = 0; i < triangleProjection.size(); ++i) { |
|
|
|
|
|
|
|
polyPoints[i] = cv::Point( |
|
|
|
|
|
|
|
static_cast<int>(std::round(triangleProjection[i].x)), |
|
|
|
|
|
|
|
static_cast<int>(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标准系数,增加数值稳定性处理
|
|
|
|
// 采用ITU-R BT.601标准系数,增加数值稳定性处理
|
|
|
|
@ -1671,146 +1732,304 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr |
|
|
|
if (itView != testFacePixelsByView.end()) { |
|
|
|
if (itView != testFacePixelsByView.end()) { |
|
|
|
const auto& facePixelsMap = itView->second; |
|
|
|
const auto& facePixelsMap = itView->second; |
|
|
|
|
|
|
|
|
|
|
|
// 打印视图信息
|
|
|
|
|
|
|
|
printf("=== 处理视图 %d: %s ===\n", idxView, strName.c_str()); |
|
|
|
printf("=== 处理视图 %d: %s ===\n", idxView, strName.c_str()); |
|
|
|
printf("这个视图有 %zu 个测试面\n", facePixelsMap.size()); |
|
|
|
printf("这个视图有 %zu 个测试面\n", facePixelsMap.size()); |
|
|
|
|
|
|
|
|
|
|
|
// 遍历这个视图中的所有测试面
|
|
|
|
// 遍历这个视图中的所有测试面
|
|
|
|
for (const auto& faceEntry : facePixelsMap) { |
|
|
|
for (const auto& faceEntry : facePixelsMap) { |
|
|
|
FIndex idxFace = faceEntry.first; |
|
|
|
FIndex idxFace = faceEntry.first; |
|
|
|
const std::vector<cv::Point>& pixels = faceEntry.second; |
|
|
|
const std::vector<cv::Point>& trianglePixels = faceEntry.second; |
|
|
|
|
|
|
|
|
|
|
|
printf(" 处理面 %d: 有 %zu 个像素\n", idxFace, pixels.size()); |
|
|
|
printf(" 处理面 %d: 有 %zu 个像素(三角形精确区域)\n", idxFace, trianglePixels.size()); |
|
|
|
|
|
|
|
|
|
|
|
// 检查像素是否为空
|
|
|
|
if (trianglePixels.empty()) { |
|
|
|
if (pixels.empty()) { |
|
|
|
|
|
|
|
printf(" ⚠️ 面 %d 没有像素,跳过\n", idxFace); |
|
|
|
printf(" ⚠️ 面 %d 没有像素,跳过\n", idxFace); |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 计算面片区域的包围盒
|
|
|
|
// 方法2:从faceMap中获取三角形的精确像素
|
|
|
|
|
|
|
|
// 这些像素已经是三角形在图像上的准确投影
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算三角形区域的边界
|
|
|
|
int minX = INT_MAX, minY = INT_MAX; |
|
|
|
int minX = INT_MAX, minY = INT_MAX; |
|
|
|
int maxX = 0, maxY = 0; |
|
|
|
int maxX = 0, maxY = 0; |
|
|
|
|
|
|
|
|
|
|
|
// 打印前几个像素的位置
|
|
|
|
// 计算三角形区域的边界
|
|
|
|
int printLimit = std::min(3, (int)pixels.size()); |
|
|
|
for (const auto& pt : trianglePixels) { |
|
|
|
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 < minX) minX = pt.x; |
|
|
|
if (pt.x > maxX) maxX = pt.x; |
|
|
|
if (pt.x > maxX) maxX = pt.x; |
|
|
|
if (pt.y < minY) minY = pt.y; |
|
|
|
if (pt.y < minY) minY = pt.y; |
|
|
|
if (pt.y > maxY) maxY = pt.y; |
|
|
|
if (pt.y > maxY) maxY = pt.y; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
printf(" 包围盒: minX=%d, maxX=%d, minY=%d, maxY=%d\n", minX, maxX, minY, maxY); |
|
|
|
printf(" 三角形区域边界: (%d,%d) 到 (%d,%d)\n", minX, minY, maxX, maxY); |
|
|
|
|
|
|
|
|
|
|
|
// 扩展一些边界
|
|
|
|
|
|
|
|
int padding = 2; |
|
|
|
|
|
|
|
int imageWidth = static_cast<int>(imageData.width); |
|
|
|
|
|
|
|
int imageHeight = static_cast<int>(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 width = maxX - minX + 1; |
|
|
|
int height = maxY - minY + 1; |
|
|
|
int height = maxY - minY + 1; |
|
|
|
|
|
|
|
|
|
|
|
printf(" 扩展后: 左上(%d,%d) 宽高(%d,%d)\n", minX, minY, width, height); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检查尺寸是否有效
|
|
|
|
|
|
|
|
if (width <= 0 || height <= 0) { |
|
|
|
if (width <= 0 || height <= 0) { |
|
|
|
printf(" ⚠️ 面 %d 无效尺寸,跳过\n", idxFace); |
|
|
|
printf(" ⚠️ 面 %d 无效尺寸,跳过\n", idxFace); |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 检查是否在图像范围内
|
|
|
|
// 创建掩码
|
|
|
|
if (minX < 0 || minX >= imageWidth || |
|
|
|
cv::Mat exactMask = cv::Mat::zeros(height, width, CV_8UC1); |
|
|
|
maxX < 0 || maxX >= imageWidth || |
|
|
|
|
|
|
|
minY < 0 || minY >= imageHeight || |
|
|
|
// 填充三角形像素
|
|
|
|
maxY < 0 || maxY >= imageHeight) { |
|
|
|
for (const auto& pt : trianglePixels) { |
|
|
|
printf(" ⚠️ 面 %d 超出图像范围,跳过\n", idxFace); |
|
|
|
int x = pt.x - minX; |
|
|
|
continue; |
|
|
|
int y = pt.y - minY; |
|
|
|
|
|
|
|
if (x >= 0 && x < width && y >= 0 && y < height) { |
|
|
|
|
|
|
|
exactMask.at<uchar>(y, x) = 255; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 提取图像区域
|
|
|
|
// 提取图像区域
|
|
|
|
cv::Rect roi(minX, minY, width, height); |
|
|
|
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(); |
|
|
|
cv::Mat imageRegion = imageData.image(roi).clone(); |
|
|
|
|
|
|
|
|
|
|
|
if (imageRegion.empty()) { |
|
|
|
// 创建带透明通道的图像
|
|
|
|
printf(" ⚠️ 面 %d 图像区域为空,跳过\n", idxFace); |
|
|
|
cv::Mat exactTriangleRegionWithAlpha = cv::Mat::zeros(height, width, CV_8UC4); |
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
|
|
// 计算三角形区域的平均颜色
|
|
|
|
|
|
|
|
cv::Scalar meanColor(0, 0, 0); |
|
|
|
|
|
|
|
int pixelCount = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 先计算平均颜色
|
|
|
|
|
|
|
|
for (int y = 0; y < height; y++) { |
|
|
|
|
|
|
|
for (int x = 0; x < width; x++) { |
|
|
|
|
|
|
|
uchar maskVal = exactMask.at<uchar>(y, x); |
|
|
|
|
|
|
|
if (maskVal > 0) { |
|
|
|
|
|
|
|
cv::Vec3b pixel = imageRegion.at<cv::Vec3b>(y, x); |
|
|
|
|
|
|
|
// 注意:OpenCV是BGR顺序
|
|
|
|
|
|
|
|
meanColor[0] += pixel[0]; // B
|
|
|
|
|
|
|
|
meanColor[1] += pixel[1]; // G
|
|
|
|
|
|
|
|
meanColor[2] += pixel[2]; // R
|
|
|
|
|
|
|
|
pixelCount++; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 填充带透明通道的图像
|
|
|
|
|
|
|
|
exactTriangleRegionWithAlpha.at<cv::Vec4b>(y, x) = |
|
|
|
|
|
|
|
cv::Vec4b(pixel[0], pixel[1], pixel[2], 255); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// 在三角形区域外:完全透明
|
|
|
|
|
|
|
|
exactTriangleRegionWithAlpha.at<cv::Vec4b>(y, x) = |
|
|
|
|
|
|
|
cv::Vec4b(0, 0, 0, 0); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
printf(" 提取图像区域成功: %dx%d\n", imageRegion.cols, imageRegion.rows); |
|
|
|
if (pixelCount > 0) { |
|
|
|
|
|
|
|
meanColor[0] /= pixelCount; // B
|
|
|
|
// 计算图像区域的平均颜色
|
|
|
|
meanColor[1] /= pixelCount; // G
|
|
|
|
cv::Scalar meanColor = cv::mean(imageRegion); |
|
|
|
meanColor[2] /= pixelCount; // R
|
|
|
|
MeshColor originalColor(meanColor[2], meanColor[1], meanColor[0]); // OpenCV是BGR格式
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 获取高斯颜色
|
|
|
|
// 获取高斯颜色
|
|
|
|
Mesh::Color gaussianColor = faceColorsGaussian[idxFace]; |
|
|
|
if (idxFace < faceColorsGaussian.size()) { |
|
|
|
MeshColor gaussianMeshColor(gaussianColor.r, gaussianColor.g, gaussianColor.b); |
|
|
|
Mesh::Color gaussianColor = faceColorsGaussian[idxFace]; |
|
|
|
|
|
|
|
MeshColor gaussianMeshColor(gaussianColor.r, gaussianColor.g, gaussianColor.b); |
|
|
|
// 计算颜色距离
|
|
|
|
|
|
|
|
float colorDistance = computeColorDifferenceHSV(gaussianMeshColor, originalColor); |
|
|
|
// 注意:meanColor是BGR顺序,需要转换为RGB
|
|
|
|
|
|
|
|
MeshColor originalColor(meanColor[2], meanColor[1], meanColor[0]); |
|
|
|
float threshold = 0.31f; // 基础阈值
|
|
|
|
|
|
|
|
bool bFilter = (colorDistance > threshold); |
|
|
|
// 计算颜色距离
|
|
|
|
|
|
|
|
float colorDistance = computeColorDifferenceHSV(gaussianMeshColor, originalColor); |
|
|
|
std::string full_path = imageData.name; |
|
|
|
float threshold = 0.31f; |
|
|
|
size_t slash_pos = full_path.find_last_of("/\\"); |
|
|
|
bool bFilter = (colorDistance > threshold); |
|
|
|
std::string filename = (slash_pos != std::string::npos) |
|
|
|
|
|
|
|
? full_path.substr(slash_pos + 1) |
|
|
|
std::string full_path = imageData.name; |
|
|
|
: full_path; |
|
|
|
size_t slash_pos = full_path.find_last_of("/\\"); |
|
|
|
size_t dot_pos = filename.find_last_of("."); |
|
|
|
std::string filename = (slash_pos != std::string::npos) |
|
|
|
if (dot_pos != std::string::npos) { |
|
|
|
? full_path.substr(slash_pos + 1) |
|
|
|
filename = filename.substr(0, dot_pos); |
|
|
|
: full_path; |
|
|
|
} |
|
|
|
size_t dot_pos = filename.find_last_of("."); |
|
|
|
|
|
|
|
if (dot_pos != std::string::npos) { |
|
|
|
printf("Test face(%d) Color: gaussian(%d,%d,%d), image(%s) color(%d,%d,%d), colorDistance(%.4f), threshold(%.4f), filter=%s\n", |
|
|
|
filename = filename.substr(0, dot_pos); |
|
|
|
idxFace, gaussianColor.r, gaussianColor.g, gaussianColor.b, |
|
|
|
} |
|
|
|
filename.c_str(), originalColor.r, originalColor.g, originalColor.b, |
|
|
|
|
|
|
|
colorDistance, threshold, bFilter ? "true" : "false"); |
|
|
|
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]); |
|
|
|
if (g_colorComparisonFace) { |
|
|
|
printf(" 原始颜色(RGB): R=%.0f, G=%.0f, B=%.0f\n", meanColor[2], meanColor[1], meanColor[0]); |
|
|
|
g_colorComparisonFace->addColorInfo(idxFace, |
|
|
|
printf(" 像素数量: %d\n", pixelCount); |
|
|
|
gaussianMeshColor, |
|
|
|
printf(" 颜色距离: %.4f, 阈值: %.4f, 过滤: %s\n", |
|
|
|
originalColor, |
|
|
|
colorDistance, threshold, bFilter ? "是" : "否"); |
|
|
|
imageRegion, |
|
|
|
|
|
|
|
colorDistance, threshold, filename); |
|
|
|
// 添加到批处理器
|
|
|
|
printf(" ✅ 面 %d 已添加到批处理器\n", idxFace); |
|
|
|
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<uchar>(y, x); |
|
|
|
|
|
|
|
if (maskVal > 0) { |
|
|
|
|
|
|
|
// 在三角形区域内:使用RGB颜色
|
|
|
|
|
|
|
|
cv::Vec3b pixel = imageRegionRGB.at<cv::Vec3b>(y, x); |
|
|
|
|
|
|
|
visualization.at<cv::Vec3b>(y, x) = pixel; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// 在三角形区域外:使用浅灰色背景
|
|
|
|
|
|
|
|
visualization.at<cv::Vec3b>(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<cv::Point> hullPoints; |
|
|
|
|
|
|
|
if (trianglePixels.size() >= 3) { |
|
|
|
|
|
|
|
// 计算凸包以获得三角形轮廓
|
|
|
|
|
|
|
|
std::vector<cv::Point> allPoints = trianglePixels; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 调整坐标到ROI
|
|
|
|
|
|
|
|
for (auto& pt : allPoints) { |
|
|
|
|
|
|
|
pt.x -= minX; |
|
|
|
|
|
|
|
pt.y -= minY; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算凸包
|
|
|
|
|
|
|
|
std::vector<cv::Point> hull; |
|
|
|
|
|
|
|
cv::convexHull(allPoints, hull); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 绘制三角形轮廓 - 增强版本
|
|
|
|
|
|
|
|
if (!hull.empty()) { |
|
|
|
|
|
|
|
// 首先绘制一个粗的蓝色轮廓
|
|
|
|
|
|
|
|
std::vector<std::vector<cv::Point>> 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<cv::Point> allPoints = trianglePixels; |
|
|
|
|
|
|
|
for (auto& pt : allPoints) { |
|
|
|
|
|
|
|
pt.x -= minX; |
|
|
|
|
|
|
|
pt.y -= minY; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<cv::Point> hull; |
|
|
|
|
|
|
|
cv::convexHull(allPoints, hull); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!hull.empty()) { |
|
|
|
|
|
|
|
std::vector<std::vector<cv::Point>> 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 |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|