|
|
|
|
@ -1002,59 +1002,30 @@ public:
@@ -1002,59 +1002,30 @@ public:
|
|
|
|
|
return sharedVertices == 2; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 修改 mergeContinuousFaces 函数的类型
|
|
|
|
|
static std::map<int, std::set<unsigned int>> mergeContinuousFaces( // 返回类型改为 unsigned int
|
|
|
|
|
const std::set<unsigned int>& testFaces, // 参数类型改为 unsigned int
|
|
|
|
|
static std::map<int, std::set<unsigned int>> mergeContinuousFaces( |
|
|
|
|
const std::set<unsigned int>& testFaces, |
|
|
|
|
const Mesh& mesh) { |
|
|
|
|
|
|
|
|
|
std::map<int, std::set<unsigned int>> regions; // 返回类型改为 unsigned int
|
|
|
|
|
std::set<unsigned int> processed; // 类型改为 unsigned int
|
|
|
|
|
int regionId = 0; |
|
|
|
|
std::map<int, std::set<unsigned int>> regions; |
|
|
|
|
|
|
|
|
|
// 遍历所有测试面
|
|
|
|
|
for (unsigned int face : testFaces) { // 类型改为 unsigned int
|
|
|
|
|
if (processed.find(face) != processed.end()) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
// 将所有测试面合并为一个区域(区域ID为0)
|
|
|
|
|
regions[0] = testFaces; // 直接将所有面放入区域0
|
|
|
|
|
|
|
|
|
|
// 开始新区域
|
|
|
|
|
std::set<unsigned int> currentRegion; // 类型改为 unsigned int
|
|
|
|
|
std::queue<unsigned int> faceQueue; // 类型改为 unsigned int
|
|
|
|
|
faceQueue.push(face); |
|
|
|
|
|
|
|
|
|
while (!faceQueue.empty()) { |
|
|
|
|
unsigned int currentFace = faceQueue.front(); // 类型改为 unsigned int
|
|
|
|
|
faceQueue.pop(); |
|
|
|
|
|
|
|
|
|
if (processed.find(currentFace) != processed.end()) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 添加到当前区域
|
|
|
|
|
currentRegion.insert(currentFace); |
|
|
|
|
processed.insert(currentFace); |
|
|
|
|
|
|
|
|
|
// 查找相邻的测试面
|
|
|
|
|
for (unsigned int otherFace : testFaces) { // 类型改为 unsigned int
|
|
|
|
|
if (processed.find(otherFace) != processed.end()) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 检查是否相邻
|
|
|
|
|
if (areFacesAdjacent(mesh.faces[currentFace], mesh.faces[otherFace])) { |
|
|
|
|
faceQueue.push(otherFace); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
printf("将所有测试面合并为一个区域\n"); |
|
|
|
|
printf("区域 0: 包含 %zu 个面\n", testFaces.size()); |
|
|
|
|
|
|
|
|
|
if (!currentRegion.empty()) { |
|
|
|
|
regions[regionId] = currentRegion; |
|
|
|
|
regionId++; |
|
|
|
|
// 输出区域包含的面
|
|
|
|
|
printf("包含的面: {"); |
|
|
|
|
for (auto it = testFaces.begin(); it != testFaces.end(); ++it) { |
|
|
|
|
printf("%u", *it); |
|
|
|
|
if (std::next(it) != testFaces.end()) { |
|
|
|
|
printf(", "); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
printf("}\n"); |
|
|
|
|
|
|
|
|
|
return regions; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//*/
|
|
|
|
|
/*
|
|
|
|
|
@ -1328,6 +1299,9 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
@@ -1328,6 +1299,9 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
|
|
|
|
|
g_colorComparisonFace->addContinuousRegionInfo(regionId, regionFaces, avgColor); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 4. 用于存储每个区域在每个视图中的像素
|
|
|
|
|
std::map<int, std::map<IIndex, std::vector<cv::Point>>> regionPixelsByView; |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
// create faces octree
|
|
|
|
|
@ -1697,7 +1671,7 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
@@ -1697,7 +1671,7 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
|
|
|
|
|
Color imageColor = Color(imageData.image(j,i)); |
|
|
|
|
// printf("idxFace(%d) imageColor:%f, %f, %f\n", idxFace, imageColor[0], imageColor[1], imageColor[2]);
|
|
|
|
|
|
|
|
|
|
Mesh::Color originalColor= Mesh::Color(imageColor[0],imageColor[1],imageColor[2]); |
|
|
|
|
Mesh::Color originalColor= Mesh::Color(imageColor[2],imageColor[1],imageColor[0]); |
|
|
|
|
|
|
|
|
|
MeshColor gaussianMeshColor(gaussianColor.r, gaussianColor.g, gaussianColor.b); |
|
|
|
|
MeshColor originalMeshColor(originalColor.r, originalColor.g, originalColor.b); |
|
|
|
|
@ -1850,6 +1824,7 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
@@ -1850,6 +1824,7 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifdef TEST |
|
|
|
|
// 处理这个视图的测试面
|
|
|
|
|
auto itView = testFacePixelsByView.find(idxView); |
|
|
|
|
if (itView != testFacePixelsByView.end()) { |
|
|
|
|
const auto& facePixelsMap = itView->second; |
|
|
|
|
@ -1862,48 +1837,63 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
@@ -1862,48 +1837,63 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
|
|
|
|
|
FIndex idxFace = faceEntry.first; |
|
|
|
|
const std::vector<cv::Point>& trianglePixels = faceEntry.second; |
|
|
|
|
|
|
|
|
|
printf(" 处理面 %d: 有 %zu 个像素(三角形精确区域)\n", idxFace, trianglePixels.size()); |
|
|
|
|
if (trianglePixels.empty()) continue; |
|
|
|
|
|
|
|
|
|
if (trianglePixels.empty()) { |
|
|
|
|
printf(" ⚠️ 面 %d 没有像素,跳过\n", idxFace); |
|
|
|
|
continue; |
|
|
|
|
// 1. 找到这个面属于哪个区域
|
|
|
|
|
int faceRegionId = -1; |
|
|
|
|
for (const auto& regionEntry : continuousRegions) { |
|
|
|
|
int regionId = regionEntry.first; |
|
|
|
|
const std::set<FIndex>& regionFaces = regionEntry.second; |
|
|
|
|
if (regionFaces.find(idxFace) != regionFaces.end()) { |
|
|
|
|
faceRegionId = regionId; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (faceRegionId == -1) continue; // 不属于任何区域
|
|
|
|
|
|
|
|
|
|
// 2. 将这个面的像素添加到区域
|
|
|
|
|
std::vector<cv::Point>& regionPixels = regionPixelsByView[faceRegionId][idxView]; |
|
|
|
|
regionPixels.insert(regionPixels.end(), trianglePixels.begin(), trianglePixels.end()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 处理每个区域在这个视图中的像素
|
|
|
|
|
for (auto& regionEntry : regionPixelsByView) { |
|
|
|
|
int regionId = regionEntry.first; |
|
|
|
|
auto& viewMap = regionEntry.second; |
|
|
|
|
|
|
|
|
|
// 方法2:从faceMap中获取三角形的精确像素
|
|
|
|
|
// 这些像素已经是三角形在图像上的准确投影
|
|
|
|
|
if (viewMap.find(idxView) == viewMap.end()) { |
|
|
|
|
continue; // 这个区域在这个视图中不可见
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 计算三角形区域的边界
|
|
|
|
|
std::vector<cv::Point>& regionPixels = viewMap[idxView]; |
|
|
|
|
if (regionPixels.empty()) continue; |
|
|
|
|
|
|
|
|
|
// 计算区域的边界
|
|
|
|
|
int minX = INT_MAX, minY = INT_MAX; |
|
|
|
|
int maxX = 0, maxY = 0; |
|
|
|
|
|
|
|
|
|
// 计算三角形区域的边界
|
|
|
|
|
for (const auto& pt : trianglePixels) { |
|
|
|
|
for (const auto& pt : regionPixels) { |
|
|
|
|
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(" 三角形区域边界: (%d,%d) 到 (%d,%d)\n", minX, minY, maxX, maxY); |
|
|
|
|
|
|
|
|
|
// 创建精确的三角形掩码
|
|
|
|
|
int width = maxX - minX + 1; |
|
|
|
|
int height = maxY - minY + 1; |
|
|
|
|
|
|
|
|
|
if (width <= 0 || height <= 0) { |
|
|
|
|
printf(" ⚠️ 面 %d 无效尺寸,跳过\n", idxFace); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
if (width <= 0 || height <= 0) continue; |
|
|
|
|
|
|
|
|
|
// 创建掩码
|
|
|
|
|
cv::Mat exactMask = cv::Mat::zeros(height, width, CV_8UC1); |
|
|
|
|
// 创建区域掩码
|
|
|
|
|
cv::Mat regionMask = cv::Mat::zeros(height, width, CV_8UC1); |
|
|
|
|
|
|
|
|
|
// 填充三角形像素
|
|
|
|
|
for (const auto& pt : trianglePixels) { |
|
|
|
|
for (const auto& pt : regionPixels) { |
|
|
|
|
int x = pt.x - minX; |
|
|
|
|
int y = pt.y - minY; |
|
|
|
|
if (x >= 0 && x < width && y >= 0 && y < height) { |
|
|
|
|
exactMask.at<uchar>(y, x) = 255; |
|
|
|
|
regionMask.at<uchar>(y, x) = 255; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -1911,108 +1901,50 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
@@ -1911,108 +1901,50 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
|
|
|
|
|
cv::Rect roi(minX, minY, width, height); |
|
|
|
|
cv::Mat imageRegion = imageData.image(roi).clone(); |
|
|
|
|
|
|
|
|
|
// 创建带透明通道的图像
|
|
|
|
|
cv::Mat exactTriangleRegionWithAlpha = cv::Mat::zeros(height, width, CV_8UC4); |
|
|
|
|
|
|
|
|
|
// 计算三角形区域的平均颜色
|
|
|
|
|
// 计算区域平均颜色
|
|
|
|
|
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); |
|
|
|
|
uchar maskVal = regionMask.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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (pixelCount > 0) { |
|
|
|
|
meanColor[0] /= pixelCount; // B
|
|
|
|
|
meanColor[1] /= pixelCount; // G
|
|
|
|
|
meanColor[2] /= pixelCount; // R
|
|
|
|
|
meanColor[0] /= pixelCount; |
|
|
|
|
meanColor[1] /= pixelCount; |
|
|
|
|
meanColor[2] /= pixelCount; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 获取高斯颜色
|
|
|
|
|
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]); |
|
|
|
|
// 创建区域可视化
|
|
|
|
|
int scaleFactor = 20; |
|
|
|
|
int enlargedWidth = width * scaleFactor; |
|
|
|
|
int enlargedHeight = height * scaleFactor; |
|
|
|
|
|
|
|
|
|
// 计算颜色距离
|
|
|
|
|
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) { |
|
|
|
|
// 设置不同的放大因子
|
|
|
|
|
int triangleScaleFactor = 30; |
|
|
|
|
int visualizationScaleFactor = 40; |
|
|
|
|
|
|
|
|
|
int triangleWidth = width * triangleScaleFactor; |
|
|
|
|
int triangleHeight = height * triangleScaleFactor; |
|
|
|
|
int enlargedWidth = width * visualizationScaleFactor; |
|
|
|
|
int enlargedHeight = height * visualizationScaleFactor; |
|
|
|
|
|
|
|
|
|
// 创建放大20倍的可视化图像(灰色背景)
|
|
|
|
|
cv::Mat visualization = cv::Mat::zeros(enlargedHeight, enlargedWidth, CV_8UC3); |
|
|
|
|
visualization.setTo(cv::Scalar(200, 200, 200)); // 浅灰色背景
|
|
|
|
|
visualization.setTo(cv::Scalar(200, 200, 200)); |
|
|
|
|
|
|
|
|
|
// 创建RGB版本的图像区域用于显示
|
|
|
|
|
cv::Mat imageRegionRGB; |
|
|
|
|
cv::cvtColor(imageRegion, imageRegionRGB, cv::COLOR_BGR2RGB); |
|
|
|
|
|
|
|
|
|
// 计算三角形在20倍图像中的居中位置
|
|
|
|
|
int offsetX = (enlargedWidth - triangleWidth) / 2; |
|
|
|
|
int offsetY = (enlargedHeight - triangleHeight) / 2; |
|
|
|
|
|
|
|
|
|
// 将三角形区域绘制到放大后的可视化图像上(居中放置)
|
|
|
|
|
for (int y = 0; y < height; y++) { |
|
|
|
|
for (int x = 0; x < width; x++) { |
|
|
|
|
uchar maskVal = exactMask.at<uchar>(y, x); |
|
|
|
|
uchar maskVal = regionMask.at<uchar>(y, x); |
|
|
|
|
if (maskVal > 0) { |
|
|
|
|
cv::Vec3b pixel = imageRegionRGB.at<cv::Vec3b>(y, x); |
|
|
|
|
|
|
|
|
|
// 将像素放大4倍,并放置在20倍图像的中央
|
|
|
|
|
for (int sy = 0; sy < triangleScaleFactor; sy++) { |
|
|
|
|
for (int sx = 0; sx < triangleScaleFactor; sx++) { |
|
|
|
|
int enlargedX = offsetX + x * triangleScaleFactor + sx; |
|
|
|
|
int enlargedY = offsetY + y * triangleScaleFactor + sy; |
|
|
|
|
for (int sy = 0; sy < scaleFactor; sy++) { |
|
|
|
|
for (int sx = 0; sx < scaleFactor; sx++) { |
|
|
|
|
int enlargedX = x * scaleFactor + sx; |
|
|
|
|
int enlargedY = y * scaleFactor + sy; |
|
|
|
|
if (enlargedX < enlargedWidth && enlargedY < enlargedHeight) { |
|
|
|
|
visualization.at<cv::Vec3b>(enlargedY, enlargedX) = pixel; |
|
|
|
|
} |
|
|
|
|
@ -2022,287 +1954,43 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
@@ -2022,287 +1954,43 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
// 立即保存放大后的visualization用于调试
|
|
|
|
|
std::string debugDir = scene.base_path + std::string("/debug_visualization/"); |
|
|
|
|
std::filesystem::create_directories(debugDir); |
|
|
|
|
std::string debugPath = debugDir + "face_" + std::to_string(idxFace) + |
|
|
|
|
"_view" + std::to_string(idxView) + "_" + filename + |
|
|
|
|
"_triangle_x" + std::to_string(triangleScaleFactor) + |
|
|
|
|
"_canvas_x" + std::to_string(visualizationScaleFactor) + ".png"; |
|
|
|
|
cv::imwrite(debugPath, visualization); |
|
|
|
|
printf(" ✅ 保存可视化图像: 三角形放大%d倍, 画布放大%d倍: %s\n", |
|
|
|
|
triangleScaleFactor, visualizationScaleFactor, 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 = offsetX + (pt.x - minX) * triangleScaleFactor; |
|
|
|
|
pt.y = offsetY + (pt.y - minY) * triangleScaleFactor; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 计算凸包
|
|
|
|
|
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++) { |
|
|
|
|
// 绘制大圆点
|
|
|
|
|
int circleRadius = 2; |
|
|
|
|
cv::circle(visualizationWithContour, hull[i], circleRadius, |
|
|
|
|
cv::Scalar(0, 255, 0), -1); |
|
|
|
|
|
|
|
|
|
// // 添加顶点编号
|
|
|
|
|
// double fontScale = 0.8;
|
|
|
|
|
// int thickness = 2;
|
|
|
|
|
// cv::putText(visualizationWithContour, std::to_string(i),
|
|
|
|
|
// cv::Point(hull[i].x + 10, hull[i].y - 10),
|
|
|
|
|
// cv::FONT_HERSHEY_SIMPLEX, fontScale,
|
|
|
|
|
// cv::Scalar(0, 0, 0), thickness);
|
|
|
|
|
|
|
|
|
|
// // 在顶点周围添加白色边框
|
|
|
|
|
// cv::circle(visualizationWithContour, hull[i], circleRadius,
|
|
|
|
|
// 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);
|
|
|
|
|
// double fontScale = 0.7;
|
|
|
|
|
// int thickness = 2;
|
|
|
|
|
// cv::putText(visualizationWithContour, faceLabel,
|
|
|
|
|
// cv::Point(center.x - 40, center.y),
|
|
|
|
|
// cv::FONT_HERSHEY_SIMPLEX, fontScale,
|
|
|
|
|
// cv::Scalar(0, 0, 0), thickness);
|
|
|
|
|
|
|
|
|
|
// // 在三角形周围添加边框
|
|
|
|
|
// cv::Rect boundingRect = cv::boundingRect(hull);
|
|
|
|
|
// cv::rectangle(visualizationWithContour, boundingRect,
|
|
|
|
|
// cv::Scalar(0, 0, 255), 2);
|
|
|
|
|
|
|
|
|
|
// // 添加比例信息文本
|
|
|
|
|
// std::string scaleInfo = "Triangle:x" + std::to_string(triangleScaleFactor) +
|
|
|
|
|
// " Canvas:x" + std::to_string(visualizationScaleFactor);
|
|
|
|
|
// cv::putText(visualizationWithContour, scaleInfo,
|
|
|
|
|
// cv::Point(10, 30), cv::FONT_HERSHEY_SIMPLEX, 0.6,
|
|
|
|
|
// cv::Scalar(0, 0, 255), 2);
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
*/ |
|
|
|
|
// 增强三角形轮廓可见性(在放大图像上)
|
|
|
|
|
cv::Mat visualizationWithContour = visualization.clone(); |
|
|
|
|
|
|
|
|
|
if (!trianglePixels.empty()) { |
|
|
|
|
// 创建三角形轮廓
|
|
|
|
|
std::vector<cv::Point> hullPoints; |
|
|
|
|
|
|
|
|
|
// 计算凸包以获得多边形轮廓
|
|
|
|
|
std::vector<cv::Point> allPoints = trianglePixels; |
|
|
|
|
|
|
|
|
|
// 调整坐标到ROI并应用三角形放大因子,然后加上偏移量
|
|
|
|
|
for (auto& pt : allPoints) { |
|
|
|
|
pt.x = offsetX + (pt.x - minX) * triangleScaleFactor; |
|
|
|
|
pt.y = offsetY + (pt.y - minY) * triangleScaleFactor; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 计算凸包
|
|
|
|
|
std::vector<cv::Point> hull; |
|
|
|
|
cv::convexHull(allPoints, hull); |
|
|
|
|
|
|
|
|
|
// 绘制三角形轮廓 - 增强版本(在放大图像上)
|
|
|
|
|
if (!hull.empty()) { |
|
|
|
|
// 计算凸包的中心点
|
|
|
|
|
cv::Point hullCenter(0, 0); |
|
|
|
|
for (const auto& p : hull) { |
|
|
|
|
hullCenter.x += p.x; |
|
|
|
|
hullCenter.y += p.y; |
|
|
|
|
} |
|
|
|
|
hullCenter.x /= hull.size(); |
|
|
|
|
hullCenter.y /= hull.size(); |
|
|
|
|
|
|
|
|
|
// 创建调整后的凸包点(移动到像素角点)
|
|
|
|
|
std::vector<cv::Point> adjustedHull; |
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < hull.size(); i++) { |
|
|
|
|
// 获取原始凸包点
|
|
|
|
|
cv::Point originalPoint = hull[i]; |
|
|
|
|
|
|
|
|
|
// 计算这个点对应的原始像素索引
|
|
|
|
|
int pixelGridX = (originalPoint.x - offsetX) / triangleScaleFactor; |
|
|
|
|
int pixelGridY = (originalPoint.y - offsetY) / triangleScaleFactor; |
|
|
|
|
|
|
|
|
|
// 计算像素的四个角点坐标
|
|
|
|
|
int pixelLeft = offsetX + pixelGridX * triangleScaleFactor; |
|
|
|
|
int pixelRight = pixelLeft + triangleScaleFactor; |
|
|
|
|
int pixelTop = offsetY + pixelGridY * triangleScaleFactor; |
|
|
|
|
int pixelBottom = pixelTop + triangleScaleFactor; |
|
|
|
|
|
|
|
|
|
// 计算像素的四个角点
|
|
|
|
|
cv::Point topLeft(pixelLeft, pixelTop); |
|
|
|
|
cv::Point topRight(pixelRight, pixelTop); |
|
|
|
|
cv::Point bottomLeft(pixelLeft, pixelBottom); |
|
|
|
|
cv::Point bottomRight(pixelRight, pixelBottom); |
|
|
|
|
|
|
|
|
|
// 根据凸包点相对于中心的位置,选择角点
|
|
|
|
|
cv::Point adjustedPoint = originalPoint; // 默认使用原始点
|
|
|
|
|
|
|
|
|
|
// 判断点在中心点的哪一侧
|
|
|
|
|
bool isRightSide = originalPoint.x > hullCenter.x; |
|
|
|
|
bool isBottomSide = originalPoint.y > hullCenter.y; |
|
|
|
|
|
|
|
|
|
if (isRightSide && !isBottomSide) { |
|
|
|
|
// 右侧上方的点 → 使用右上角
|
|
|
|
|
adjustedPoint = topRight; |
|
|
|
|
} else if (isRightSide && isBottomSide) { |
|
|
|
|
// 右侧下方的点 → 使用右下角
|
|
|
|
|
adjustedPoint = bottomRight; |
|
|
|
|
} else if (!isRightSide && isBottomSide) { |
|
|
|
|
// 左侧下方的点 → 使用左下角
|
|
|
|
|
adjustedPoint = bottomLeft; |
|
|
|
|
} else if (!isRightSide && !isBottomSide) { |
|
|
|
|
// 左侧上方的点 → 使用左上角
|
|
|
|
|
adjustedPoint = topLeft; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
adjustedHull.push_back(adjustedPoint); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 确保adjustedHull是闭合的(首尾相连)
|
|
|
|
|
if (!adjustedHull.empty() && adjustedHull[0] != adjustedHull.back()) { |
|
|
|
|
adjustedHull.push_back(adjustedHull[0]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 使用调整后的点绘制多边形轮廓
|
|
|
|
|
if (adjustedHull.size() >= 2) { |
|
|
|
|
// 绘制白色轮廓线
|
|
|
|
|
for (size_t i = 0; i < adjustedHull.size() - 1; i++) { |
|
|
|
|
cv::line(visualizationWithContour, |
|
|
|
|
adjustedHull[i], |
|
|
|
|
adjustedHull[i + 1], |
|
|
|
|
cv::Scalar(255, 255, 255), // 白色
|
|
|
|
|
1); // 线宽
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 标记顶点
|
|
|
|
|
for (size_t i = 0; i < adjustedHull.size() - 1; i++) { |
|
|
|
|
int circleRadius = 3; |
|
|
|
|
cv::circle(visualizationWithContour, |
|
|
|
|
adjustedHull[i], |
|
|
|
|
circleRadius, |
|
|
|
|
cv::Scalar(0, 255, 0), // 绿色
|
|
|
|
|
-1); // 实心圆
|
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
// 如果调整后的点集有问题,回退到原始凸包
|
|
|
|
|
std::vector<std::vector<cv::Point>> contours = {hull}; |
|
|
|
|
cv::drawContours(visualizationWithContour, contours, 0, |
|
|
|
|
cv::Scalar(255, 255, 255), 1); |
|
|
|
|
|
|
|
|
|
// 标记顶点
|
|
|
|
|
for (size_t i = 0; i < hull.size(); i++) { |
|
|
|
|
int circleRadius = 2; |
|
|
|
|
cv::circle(visualizationWithContour, hull[i], circleRadius, |
|
|
|
|
cv::Scalar(0, 255, 0), -1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建放大后的带透明通道的图像(三角形放大4倍,画布放大20倍)
|
|
|
|
|
cv::Mat exactTriangleRegionWithContour = cv::Mat::zeros(enlargedHeight, enlargedWidth, CV_8UC4); |
|
|
|
|
// 在可视化图像上添加区域信息
|
|
|
|
|
// std::string regionInfo = cv::format("Region %d: %d faces, %d pixels",
|
|
|
|
|
// regionId, continuousRegions[regionId].size(), pixelCount);
|
|
|
|
|
// cv::putText(visualization, regionInfo,
|
|
|
|
|
// cv::Point(20, 30),
|
|
|
|
|
// cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(0, 0, 0), 2);
|
|
|
|
|
|
|
|
|
|
// 将原始透明图像放大,三角形区域放大4倍并居中
|
|
|
|
|
// 创建带透明通道的区域图像
|
|
|
|
|
cv::Mat regionWithAlpha = cv::Mat::zeros(height, width, CV_8UC4); |
|
|
|
|
for (int y = 0; y < height; y++) { |
|
|
|
|
for (int x = 0; x < width; x++) { |
|
|
|
|
cv::Vec4b pixel = exactTriangleRegionWithAlpha.at<cv::Vec4b>(y, x); |
|
|
|
|
|
|
|
|
|
// 只复制三角形区域(非透明部分)
|
|
|
|
|
if (pixel[3] > 0) { |
|
|
|
|
// 将像素放大4倍,并放置在20倍图像的中央
|
|
|
|
|
for (int sy = 0; sy < triangleScaleFactor; sy++) { |
|
|
|
|
for (int sx = 0; sx < triangleScaleFactor; sx++) { |
|
|
|
|
int enlargedX = offsetX + x * triangleScaleFactor + sx; |
|
|
|
|
int enlargedY = offsetY + y * triangleScaleFactor + sy; |
|
|
|
|
if (enlargedX < enlargedWidth && enlargedY < enlargedHeight) { |
|
|
|
|
exactTriangleRegionWithContour.at<cv::Vec4b>(enlargedY, enlargedX) = pixel; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
uchar maskVal = regionMask.at<uchar>(y, x); |
|
|
|
|
if (maskVal > 0) { |
|
|
|
|
cv::Vec3b pixel = imageRegion.at<cv::Vec3b>(y, x); |
|
|
|
|
regionWithAlpha.at<cv::Vec4b>(y, x) = |
|
|
|
|
cv::Vec4b(pixel[0], pixel[1], pixel[2], 255); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 在放大的透明图像上也绘制轮廓
|
|
|
|
|
if (trianglePixels.size() >= 3) { |
|
|
|
|
std::vector<cv::Point> allPoints = trianglePixels; |
|
|
|
|
for (auto& pt : allPoints) { |
|
|
|
|
pt.x = offsetX + (pt.x - minX) * triangleScaleFactor; |
|
|
|
|
pt.y = offsetY + (pt.y - minY) * triangleScaleFactor; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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); // 蓝色轮廓,不透明
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 传递放大后的带透明通道的三角形区域
|
|
|
|
|
g_colorComparisonFace->addExactTriangleInfo(idxFace, |
|
|
|
|
gaussianMeshColor, |
|
|
|
|
originalColor, |
|
|
|
|
exactTriangleRegionWithContour, // 传递放大后的带轮廓透明通道图像
|
|
|
|
|
visualizationWithContour, // 传递带轮廓的可视化图像
|
|
|
|
|
colorDistance, threshold, filename); |
|
|
|
|
// 计算颜色距离
|
|
|
|
|
MeshColor regionGaussianColor = regionGaussianColors[regionId]; |
|
|
|
|
MeshColor viewColor(meanColor[2], meanColor[1], meanColor[0]); |
|
|
|
|
float colorDistance = computeColorDifferenceHSV(regionGaussianColor, viewColor); |
|
|
|
|
|
|
|
|
|
printf(" ✅ 面 %d 已添加到批处理器(三角形放大%d倍,画布放大%d倍)\n", |
|
|
|
|
idxFace, triangleScaleFactor, visualizationScaleFactor); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// 添加到批处理器
|
|
|
|
|
g_colorComparisonFace->addRegionViewInfo( |
|
|
|
|
regionId, |
|
|
|
|
strName, // 视图文件名
|
|
|
|
|
viewColor, |
|
|
|
|
regionWithAlpha, |
|
|
|
|
visualization, |
|
|
|
|
colorDistance |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
printf("=== 视图 %d 处理完成 ===\n\n", idxView); |
|
|
|
|
} else { |
|
|
|
|
printf("视图 %d: 没有测试面\n", idxView); |
|
|
|
|
printf(" 区域 %d 在视图 %s: %d 像素, 颜色距离=%.4f\n", |
|
|
|
|
regionId, strName.c_str(), pixelCount, colorDistance); |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
@ -2348,8 +2036,13 @@ if (!trianglePixels.empty()) {
@@ -2348,8 +2036,13 @@ if (!trianglePixels.empty()) {
|
|
|
|
|
|
|
|
|
|
#ifdef TEST |
|
|
|
|
if (g_colorComparisonFace) { |
|
|
|
|
// 创建实际图像区域对比图
|
|
|
|
|
printf("创建实际图像区域对比图,包含 %d 个face,%d 条记录\n", |
|
|
|
|
printf("创建连续区域跨视图比较图,包含 %d 个区域\n", |
|
|
|
|
g_colorComparisonFace->getTotalRegions()); |
|
|
|
|
|
|
|
|
|
g_colorComparisonFace->createContinuousRegionComparison(6, 20); |
|
|
|
|
|
|
|
|
|
// 原有的单面比较
|
|
|
|
|
printf("创建单面对比图,包含 %d 个face,%d 条记录\n", |
|
|
|
|
g_colorComparisonFace->getTotalFaces(), g_colorComparisonFace->getTotalRecords()); |
|
|
|
|
|
|
|
|
|
g_colorComparisonFace->createBatchComparison(10, 500); |
|
|
|
|
@ -2358,7 +2051,7 @@ if (!trianglePixels.empty()) {
@@ -2358,7 +2051,7 @@ if (!trianglePixels.empty()) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (g_colorComparisonPixel) { |
|
|
|
|
g_colorComparisonPixel->createBatchComparison(10, 500); // 每行4个,最多20个
|
|
|
|
|
g_colorComparisonPixel->createBatchComparison(10, 500); |
|
|
|
|
delete g_colorComparisonPixel; |
|
|
|
|
g_colorComparisonPixel = nullptr; |
|
|
|
|
} |
|
|
|
|
|