Compare commits

...

1 Commits

Author SHA1 Message Date
hesuicong e8e84643f6 添加视图比对 9 hours ago
  1. 6
      apps/TextureMesh/TextureMesh.cpp
  2. 10
      libs/Common/Util.inl
  3. 428
      libs/MVS/ColorComparisonFace.cpp
  4. 70
      libs/MVS/ColorComparisonFace.h
  5. 505
      libs/MVS/ColorComparisonPixel.cpp
  6. 74
      libs/MVS/ColorComparisonPixel.h
  7. 45
      libs/MVS/Mesh.cpp
  8. 16
      libs/MVS/Mesh.h
  9. 4
      libs/MVS/Scene.h
  10. 787
      libs/MVS/SceneTexture.cpp

6
apps/TextureMesh/TextureMesh.cpp

@ -1098,7 +1098,9 @@ int main(int argc, LPCTSTR* argv)
return EXIT_FAILURE; return EXIT_FAILURE;
printf("Now\n"); printf("Now\n");
VERBOSE(MAKE_PATH_SAFE(OPT::strMeshFileName)); VERBOSE(MAKE_PATH_SAFE(OPT::strMeshFileName));
if (!OPT::strMeshFileName.empty() && !scene.mesh.Load(MAKE_PATH_SAFE(OPT::strMeshFileName))) { bool bVertexColorExists = false;
bool *pBExistVertexColor = &bVertexColorExists;
if (!OPT::strMeshFileName.empty() && !scene.mesh.Load(MAKE_PATH_SAFE(OPT::strMeshFileName), pBExistVertexColor)) {
VERBOSE("error: cannot load mesh file"); VERBOSE("error: cannot load mesh file");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -1134,7 +1136,7 @@ int main(int argc, LPCTSTR* argv)
if (!scene.TextureMesh(OPT::nResolutionLevel, OPT::nMinResolution, OPT::minCommonCameras, OPT::fOutlierThreshold, OPT::fRatioDataSmoothness, if (!scene.TextureMesh(OPT::nResolutionLevel, OPT::nMinResolution, OPT::minCommonCameras, OPT::fOutlierThreshold, OPT::fRatioDataSmoothness,
OPT::bGlobalSeamLeveling, OPT::bLocalSeamLeveling, OPT::nTextureSizeMultiple, OPT::nRectPackingHeuristic, Pixel8U(OPT::nColEmpty), OPT::bGlobalSeamLeveling, OPT::bLocalSeamLeveling, OPT::nTextureSizeMultiple, OPT::nRectPackingHeuristic, Pixel8U(OPT::nColEmpty),
OPT::fSharpnessWeight, OPT::nIgnoreMaskLabel, OPT::nMaxTextureSize, views, baseFileName, OPT::bOriginFaceview, OPT::fSharpnessWeight, OPT::nIgnoreMaskLabel, OPT::nMaxTextureSize, views, baseFileName, OPT::bOriginFaceview,
OPT::strInputFileName, OPT::strMeshFileName)) OPT::strInputFileName, OPT::strMeshFileName, *pBExistVertexColor))
return EXIT_FAILURE; return EXIT_FAILURE;
VERBOSE("Mesh texturing completed: %u vertices, %u faces (%s)", scene.mesh.vertices.GetSize(), scene.mesh.faces.GetSize(), TD_TIMER_GET_FMT().c_str()); VERBOSE("Mesh texturing completed: %u vertices, %u faces (%s)", scene.mesh.vertices.GetSize(), scene.mesh.faces.GetSize(), TD_TIMER_GET_FMT().c_str());

10
libs/Common/Util.inl

@ -579,6 +579,16 @@ inline TPoint3<TYPE> ComputeTriangleNormal(const TPoint3<TYPE>& v0, const TPoint
} // ComputeTriangleNormal } // ComputeTriangleNormal
/*----------------------------------------------------------------*/ /*----------------------------------------------------------------*/
template<typename ColorType>
ColorType ComputeTriangleColor(const ColorType& c0, const ColorType& c1, const ColorType& c2) {
// 手动计算每个通道的平均值
ColorType result;
result.r = static_cast<uint8_t>((static_cast<int>(c0.r) + c1.r + c2.r) / 3 + 0.5f); // 整数运算避免溢出
result.g = static_cast<uint8_t>((c0.g + c1.g + c2.g) / 3 + 0.5f);
result.b = static_cast<uint8_t>((c0.b + c1.b + c2.b) / 3 + 0.5f);
return result;
}
// compute the area of a triangle using Heron's formula // compute the area of a triangle using Heron's formula
template <typename TYPE> template <typename TYPE>
TYPE ComputeTriangleAreaSqLen(TYPE lena, TYPE lenb, TYPE lenc) { TYPE ComputeTriangleAreaSqLen(TYPE lena, TYPE lenb, TYPE lenc) {

428
libs/MVS/ColorComparisonFace.cpp

@ -0,0 +1,428 @@
#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());
}
}

70
libs/MVS/ColorComparisonFace.h

@ -0,0 +1,70 @@
#ifndef COLORCOMPARISONBFACE_H
#define COLORCOMPARISONBFACE_H
#include <string>
#include <vector>
#include <map>
// 不使用前向声明,直接定义简单的颜色结构
struct MeshColor {
unsigned char r, g, b;
MeshColor() : r(0), g(0), b(0) {}
MeshColor(unsigned char r, unsigned char g, unsigned char b)
: r(r), g(g), b(b) {}
unsigned char operator[](size_t idx) const {
if (idx == 0) return r;
if (idx == 1) return g;
if (idx == 2) return b;
return 0;
}
};
class ColorComparisonFace {
private:
struct ColorInfo {
int faceId;
MeshColor gaussianColor;
cv::Mat imageRegion; // 存储实际图像区域
float distance;
float threshold;
std::string filename;
};
// 使用嵌套map: faceid -> filename -> ColorInfo列表
std::map<int, std::map<std::string, std::vector<ColorInfo>>> faceViewColorMap;
std::string outputDir;
public:
// 构造函数
ColorComparisonFace(const std::string& dir);
// 析构函数
virtual ~ColorComparisonFace() {}
// 添加颜色信息(带图像区域)
void addColorInfo(int faceId,
const MeshColor& gaussianColor,
const cv::Mat& imageRegion,
float distance, float threshold,
const std::string& filename);
// 创建实际图像区域对比图
void createBatchComparison(int maxBlocksPerRow = 6, int maxFacesPerImage = 20);
// 保存颜色信息到CSV文件
void saveColorInfoToFile();
// 获取总face数
int getTotalFaces() const;
// 获取总记录数
int getTotalRecords() const;
// 获取faceid列表
std::vector<int> getFaceIds() const;
};
#endif // COLORCOMPARISONBFACE_H

505
libs/MVS/ColorComparisonPixel.cpp

@ -0,0 +1,505 @@
#include "ColorComparisonPixel.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;
}
// 构造函数
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<int> ColorComparisonPixel::getFaceIds() const {
std::vector<int> 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<std::pair<std::string, int>> viewRowsInfo; // 存储每个视图需要的行数
for (const auto& viewEntry : viewMap) {
const std::string& viewName = viewEntry.first;
const std::vector<ColorInfo>& 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<ColorInfo>& 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<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,%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<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());
}
}

74
libs/MVS/ColorComparisonPixel.h

@ -0,0 +1,74 @@
#ifndef COLORCOMPARISONPIXEL_H
#define COLORCOMPARISONPIXEL_H
#include <string>
#include <vector>
#include <map>
#include <unordered_map>
#include "ColorComparisonFace.h"
/*
// 不使用前向声明,直接定义简单的颜色结构
struct MeshColor {
unsigned char r, g, b;
MeshColor() : r(0), g(0), b(0) {}
MeshColor(unsigned char r, unsigned char g, unsigned char b)
: r(r), g(g), b(b) {}
unsigned char operator[](size_t idx) const {
if (idx == 0) return r;
if (idx == 1) return g;
if (idx == 2) return b;
return 0;
}
};
*/
class ColorComparisonPixel {
private:
struct ColorInfo {
int faceId;
MeshColor gaussianColor;
MeshColor originalColor;
float distance;
float threshold;
std::string filename;
};
// 使用嵌套map: faceid -> filename -> ColorInfo列表
std::map<int, std::map<std::string, std::vector<ColorInfo>>> faceViewColorMap;
std::string outputDir;
public:
// 构造函数
ColorComparisonPixel(const std::string& dir);
// 析构函数
virtual ~ColorComparisonPixel() {}
// 添加颜色信息
void addColorInfo(int faceId,
const MeshColor& gaussianColor,
const MeshColor& originalColor,
float distance, float threshold,
const std::string& filename);
// 创建按faceid分组的对比图
void createBatchComparison(int maxCellsPerRow = 10, int maxFacesPerImage = 50);
// 保存颜色信息到CSV文件
void saveColorInfoToFile();
// 获取总face数
int getTotalFaces() const;
// 获取总记录数
int getTotalRecords() const;
// 获取faceid列表
std::vector<int> getFaceIds() const;
};
#endif // COLORCOMPARISONPIXEL_H

45
libs/MVS/Mesh.cpp

@ -350,6 +350,16 @@ void Mesh::ComputeNormalFaces()
#endif #endif
} }
// compute color for all faces
void Mesh::ComputeColorFaces()
{
faceColors.resize(faces.size());
FOREACH(idxFace, faces)
faceColors[idxFace] = FaceColor(faces[idxFace]);
}
// compute normal for all vertices // compute normal for all vertices
#if 1 #if 1
// computes the vertex normal as the area weighted face normals average // computes the vertex normal as the area weighted face normals average
@ -1096,18 +1106,20 @@ namespace BasicPLY {
struct Vertex { struct Vertex {
Mesh::Vertex v; Mesh::Vertex v;
Mesh::Normal n; Mesh::Normal n;
Mesh::Color color;
static void InitLoadProps(PLY& ply, int elem_count, static void InitLoadProps(PLY& ply, int elem_count,
Mesh::VertexArr& vertices, Mesh::NormalArr& vertexNormals) Mesh::VertexArr& vertices, Mesh::ColorArr& vertexColors)
{ {
PLY::PlyElement* elm = ply.find_element(elem_names[0]); PLY::PlyElement* elm = ply.find_element(elem_names[0]);
const size_t nMaxProps(SizeOfArray(props)); const size_t nMaxProps(SizeOfArray(props));
for (size_t p=0; p<nMaxProps; ++p) { for (size_t p=0; p<nMaxProps; ++p) {
// DEBUG_EXTRA("InitLoadProps name=%s\n", props[p].name.c_str());
if (ply.find_property(elm, props[p].name.c_str()) < 0) if (ply.find_property(elm, props[p].name.c_str()) < 0)
continue; continue;
ply.setup_property(props[p]); ply.setup_property(props[p]);
switch (p) { switch (p) {
case 0: vertices.resize((IDX)elem_count); break; case 0: vertices.resize((IDX)elem_count); break;
case 3: vertexNormals.resize((IDX)elem_count); break; case 3: vertexColors.resize((IDX)elem_count); break;
} }
} }
} }
@ -1125,6 +1137,15 @@ namespace BasicPLY {
} }
static const PLY::PlyProperty props[9]; static const PLY::PlyProperty props[9];
}; };
const PLY::PlyProperty Vertex::props[] = {
{"x", PLY::Float32, PLY::Float32, offsetof(Vertex,v.x), 0, 0, 0, 0},
{"y", PLY::Float32, PLY::Float32, offsetof(Vertex,v.y), 0, 0, 0, 0},
{"z", PLY::Float32, PLY::Float32, offsetof(Vertex,v.z), 0, 0, 0, 0},
{"red", PLY::Int8, PLY::Int8, offsetof(Vertex,color.r), 0, 0, 0, 0},
{"green", PLY::Int8, PLY::Int8, offsetof(Vertex,color.g), 0, 0, 0, 0},
{"blue", PLY::Int8, PLY::Int8, offsetof(Vertex,color.b), 0, 0, 0, 0}
};
/*
const PLY::PlyProperty Vertex::props[] = { const PLY::PlyProperty Vertex::props[] = {
{"x", PLY::Float32, PLY::Float32, offsetof(Vertex,v.x), 0, 0, 0, 0}, {"x", PLY::Float32, PLY::Float32, offsetof(Vertex,v.x), 0, 0, 0, 0},
{"y", PLY::Float32, PLY::Float32, offsetof(Vertex,v.y), 0, 0, 0, 0}, {"y", PLY::Float32, PLY::Float32, offsetof(Vertex,v.y), 0, 0, 0, 0},
@ -1133,6 +1154,7 @@ namespace BasicPLY {
{"ny", PLY::Float32, PLY::Float32, offsetof(Vertex,n.y), 0, 0, 0, 0}, {"ny", PLY::Float32, PLY::Float32, offsetof(Vertex,n.y), 0, 0, 0, 0},
{"nz", PLY::Float32, PLY::Float32, offsetof(Vertex,n.z), 0, 0, 0, 0} {"nz", PLY::Float32, PLY::Float32, offsetof(Vertex,n.z), 0, 0, 0, 0}
}; };
*/
// list of property information for a face // list of property information for a face
struct Face { struct Face {
struct FaceIndices { struct FaceIndices {
@ -1191,7 +1213,7 @@ namespace BasicPLY {
} // namespace MeshInternal } // namespace MeshInternal
// import the mesh from the given file // import the mesh from the given file
bool Mesh::Load(const String& fileName) bool Mesh::Load(const String& fileName, bool *pBExistVertexColor)
{ {
TD_TIMER_STARTD(); TD_TIMER_STARTD();
const String ext(Util::getFileExt(fileName).ToLower()); const String ext(Util::getFileExt(fileName).ToLower());
@ -1202,14 +1224,14 @@ bool Mesh::Load(const String& fileName)
if (ext == _T(".gltf") || ext == _T(".glb")) if (ext == _T(".gltf") || ext == _T(".glb"))
ret = LoadGLTF(fileName, ext == _T(".glb")); ret = LoadGLTF(fileName, ext == _T(".glb"));
else else
ret = LoadPLY(fileName); ret = LoadPLY(fileName, pBExistVertexColor);
if (!ret) if (!ret)
return false; return false;
DEBUG_EXTRA("Mesh loaded: %u vertices, %u faces (%s)", vertices.size(), faces.size(), TD_TIMER_GET_FMT().c_str()); DEBUG_EXTRA("Mesh loaded: %u vertices, %u faces (%s)", vertices.size(), faces.size(), TD_TIMER_GET_FMT().c_str());
return true; return true;
} }
// import the mesh as a PLY file // import the mesh as a PLY file
bool Mesh::LoadPLY(const String& fileName) bool Mesh::LoadPLY(const String& fileName, bool *pBExistVertexColor)
{ {
ASSERT(!fileName.empty()); ASSERT(!fileName.empty());
Release(); Release();
@ -1244,16 +1266,23 @@ bool Mesh::LoadPLY(const String& fileName)
LPCSTR elem_name = ply.setup_element_read(i, &elem_count); LPCSTR elem_name = ply.setup_element_read(i, &elem_count);
if (PLY::equal_strings(BasicPLY::elem_names[0], elem_name)) { if (PLY::equal_strings(BasicPLY::elem_names[0], elem_name)) {
ASSERT(vertices.size() == (VIndex)elem_count); ASSERT(vertices.size() == (VIndex)elem_count);
BasicPLY::Vertex::InitLoadProps(ply, elem_count, vertices, vertexNormals); // BasicPLY::Vertex::InitLoadProps(ply, elem_count, vertices, vertexNormals);
if (vertexNormals.empty()) { BasicPLY::Vertex::InitLoadProps(ply, elem_count, vertices, vertexColors);
if (vertexColors.empty()) {
// printf("vertexColors.empty, elem_count=%u\n", elem_count);
*pBExistVertexColor = false;
for (Vertex& vert: vertices) for (Vertex& vert: vertices)
ply.get_element(&vert); ply.get_element(&vert);
} else { } else {
// printf("Not vertexColors.empty, elem_count=%u\n", elem_count);
*pBExistVertexColor = true;
BasicPLY::Vertex vertex; BasicPLY::Vertex vertex;
for (int v=0; v<elem_count; ++v) { for (int v=0; v<elem_count; ++v) {
ply.get_element(&vertex); ply.get_element(&vertex);
vertices[v] = vertex.v; vertices[v] = vertex.v;
vertexNormals[v] = vertex.n; vertexColors[v] = vertex.color;
// if (v<10)
// printf("vertexColors v=%u, rgb=(%u, %u, %u)\n", v, vertex.color.r, vertex.color.g, vertex.color.b);
} }
} }
} else } else

16
libs/MVS/Mesh.h

@ -61,6 +61,9 @@ public:
typedef SEACAVE::cList<Vertex,const Vertex&,0,8192,VIndex> VertexArr; typedef SEACAVE::cList<Vertex,const Vertex&,0,8192,VIndex> VertexArr;
typedef SEACAVE::cList<Face,const Face&,0,8192,FIndex> FaceArr; typedef SEACAVE::cList<Face,const Face&,0,8192,FIndex> FaceArr;
typedef Color8U Color;
typedef SEACAVE::cList<Color,const Color&,0,8192,VIndex> ColorArr;
typedef SEACAVE::cList<VIndex,VIndex,0,8,VIndex> VertexIdxArr; typedef SEACAVE::cList<VIndex,VIndex,0,8,VIndex> VertexIdxArr;
typedef SEACAVE::cList<FIndex,FIndex,0,8,FIndex> FaceIdxArr; typedef SEACAVE::cList<FIndex,FIndex,0,8,FIndex> FaceIdxArr;
typedef SEACAVE::cList<VertexIdxArr,const VertexIdxArr&,2,8192,VIndex> VertexVerticesArr; typedef SEACAVE::cList<VertexIdxArr,const VertexIdxArr&,2,8192,VIndex> VertexVerticesArr;
@ -154,6 +157,9 @@ public:
VertexFacesArr vertexFaces; // for each vertex, the ordered list of faces containing it (optional) VertexFacesArr vertexFaces; // for each vertex, the ordered list of faces containing it (optional)
BoolArr vertexBoundary; // for each vertex, stores if it is at the boundary or not (optional) BoolArr vertexBoundary; // for each vertex, stores if it is at the boundary or not (optional)
ColorArr vertexColors; // for each vertex, the normal to the surface in that point (optional)
ColorArr faceColors; // for each face, the color to it
NormalArr faceNormals; // for each face, the normal to it (optional) NormalArr faceNormals; // for each face, the normal to it (optional)
FaceFacesArr faceFaces; // for each face, the list of adjacent faces, NO_ID for border edges (optional) FaceFacesArr faceFaces; // for each face, the list of adjacent faces, NO_ID for border edges (optional)
TexCoordArr faceTexcoords; // for each face, the texture-coordinates corresponding to its vertices, 3x num faces OR for each vertex (optional) TexCoordArr faceTexcoords; // for each face, the texture-coordinates corresponding to its vertices, 3x num faces OR for each vertex (optional)
@ -194,6 +200,8 @@ public:
void ComputeNormalFaces(); void ComputeNormalFaces();
void ComputeNormalVertices(); void ComputeNormalVertices();
void ComputeColorFaces();
void SmoothNormalFaces(float fMaxGradient=25.f, float fOriginalWeight=0.5f, unsigned nIterations=3); void SmoothNormalFaces(float fMaxGradient=25.f, float fOriginalWeight=0.5f, unsigned nIterations=3);
void GetEdgeFaces(VIndex, VIndex, FaceIdxArr&) const; void GetEdgeFaces(VIndex, VIndex, FaceIdxArr&) const;
@ -238,6 +246,10 @@ public:
return n; return n;
} }
inline Color FaceColor(const Face& f) const {
return ComputeTriangleColor(vertexColors[f[0]], vertexColors[f[1]], vertexColors[f[2]]);
}
Planef EstimateGroundPlane(const ImageArr& images, float sampleMesh=0, float planeThreshold=0, const String& fileExportPlane="") const; Planef EstimateGroundPlane(const ImageArr& images, float sampleMesh=0, float planeThreshold=0, const String& fileExportPlane="") const;
Vertex ComputeCentroid(FIndex) const; Vertex ComputeCentroid(FIndex) const;
@ -262,7 +274,7 @@ public:
bool TransferTexture(Mesh& mesh, const FaceIdxArr& faceSubsetIndices={}, unsigned borderSize=3, unsigned textureSize=4096); bool TransferTexture(Mesh& mesh, const FaceIdxArr& faceSubsetIndices={}, unsigned borderSize=3, unsigned textureSize=4096);
// file IO // file IO
bool Load(const String& fileName); bool Load(const String& fileName, bool *pBExistVertexColor=nullptr);
bool Save(const String& fileName, const cList<String>& comments=cList<String>(), bool bBinary=true) const; bool Save(const String& fileName, const cList<String>& comments=cList<String>(), bool bBinary=true) const;
bool Save(const FacesChunkArr&, const String& fileName, const cList<String>& comments=cList<String>(), bool bBinary=true) const; bool Save(const FacesChunkArr&, const String& fileName, const cList<String>& comments=cList<String>(), bool bBinary=true) const;
static bool Save(const VertexArr& vertices, const String& fileName, bool bBinary=true); static bool Save(const VertexArr& vertices, const String& fileName, bool bBinary=true);
@ -272,7 +284,7 @@ public:
static inline VIndex& GetVertex(Face& f, VIndex v) { const uint32_t idx(FindVertex(f, v)); ASSERT(idx != NO_ID); return f[idx]; } static inline VIndex& GetVertex(Face& f, VIndex v) { const uint32_t idx(FindVertex(f, v)); ASSERT(idx != NO_ID); return f[idx]; }
protected: protected:
bool LoadPLY(const String& fileName); bool LoadPLY(const String& fileName, bool *pBExistVertexColor);
bool LoadOBJ(const String& fileName); bool LoadOBJ(const String& fileName);
bool LoadGLTF(const String& fileName, bool bBinary=true); bool LoadGLTF(const String& fileName, bool bBinary=true);

4
libs/MVS/Scene.h

@ -66,6 +66,7 @@ public:
std::map<std::string, std::unordered_set<int>> edge_faces_map; std::map<std::string, std::unordered_set<int>> edge_faces_map;
std::map<std::string, std::unordered_set<int>> delete_edge_faces_map; std::map<std::string, std::unordered_set<int>> delete_edge_faces_map;
std::unordered_set<int> face_visible_relative; std::unordered_set<int> face_visible_relative;
std::unordered_set<int> face_test;
public: public:
inline Scene(unsigned _nMaxThreads=0) inline Scene(unsigned _nMaxThreads=0)
@ -164,12 +165,13 @@ public:
std::map<std::string, std::unordered_set<int>>& edge_faces_map, std::map<std::string, std::unordered_set<int>>& edge_faces_map,
std::map<std::string, std::unordered_set<int>>& delete_edge_faces_map, std::map<std::string, std::unordered_set<int>>& delete_edge_faces_map,
std::string& basePath); std::string& basePath);
bool LoadTestFacesData(std::unordered_set<int>& face_test, std::string& basePath);
// Mesh texturing // Mesh texturing
bool TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsigned minCommonCameras=0, float fOutlierThreshold=0.f, float fRatioDataSmoothness=0.3f, bool TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsigned minCommonCameras=0, float fOutlierThreshold=0.f, float fRatioDataSmoothness=0.3f,
bool bGlobalSeamLeveling=true, bool bLocalSeamLeveling=true, unsigned nTextureSizeMultiple=0, unsigned nRectPackingHeuristic=3, Pixel8U colEmpty=Pixel8U(255,127,39), bool bGlobalSeamLeveling=true, bool bLocalSeamLeveling=true, unsigned nTextureSizeMultiple=0, unsigned nRectPackingHeuristic=3, Pixel8U colEmpty=Pixel8U(255,127,39),
float fSharpnessWeight=0.5f, int ignoreMaskLabel=-1, int maxTextureSize=0, const IIndexArr& views=IIndexArr(), const SEACAVE::String& basename = "", bool bOriginFaceview = false, float fSharpnessWeight=0.5f, int ignoreMaskLabel=-1, int maxTextureSize=0, const IIndexArr& views=IIndexArr(), const SEACAVE::String& basename = "", bool bOriginFaceview = false,
const std::string& inputFileName = "", const std::string& meshFileName = ""); const std::string& inputFileName = "", const std::string& meshFileName = "", bool bExistVertexColor = false);
std::string runPython(const std::string& command); std::string runPython(const std::string& command);
bool is_face_visible(const std::string& image_name, int face_index); bool is_face_visible(const std::string& image_name, int face_index);

787
libs/MVS/SceneTexture.cpp

@ -43,6 +43,8 @@
#include "cuda/MeshTextureCUDA.h" #include "cuda/MeshTextureCUDA.h"
#include <sstream> #include <sstream>
#include <filesystem> #include <filesystem>
#include "ColorComparisonFace.h"
#include "ColorComparisonPixel.h"
namespace fs = std::filesystem; namespace fs = std::filesystem;
@ -76,6 +78,8 @@ using namespace MVS;
#define TEXOPT_INFERENCE_TRWS 2 #define TEXOPT_INFERENCE_TRWS 2
#define TEXOPT_INFERENCE TEXOPT_INFERENCE_LBP #define TEXOPT_INFERENCE TEXOPT_INFERENCE_LBP
#define MASK_FACE_OCCLUSION #define MASK_FACE_OCCLUSION
// #define TEST
// #define USE_CUDA // #define USE_CUDA
// inference algorithm // inference algorithm
@ -590,6 +594,336 @@ public:
return distances[distances.size() / 2]; return distances[distances.size() / 2];
} }
static Color computeMedianColor(const std::vector<Color>& colors) {
if (colors.empty()) return Color::ZERO;
// 分别计算RGB通道的中位数
std::vector<float> rVals, gVals, bVals;
rVals.reserve(colors.size());
gVals.reserve(colors.size());
bVals.reserve(colors.size());
for (const Color& c : colors) {
rVals.push_back(c.z);
gVals.push_back(c.y);
bVals.push_back(c.x);
}
auto getMedian = [](std::vector<float>& vals) {
size_t n = vals.size() / 2;
std::nth_element(vals.begin(), vals.begin() + n, vals.end());
float median = vals[n];
if (vals.size() % 2 == 0) {
std::nth_element(vals.begin(), vals.begin() + n - 1, vals.end());
median = (median + vals[n-1]) * 0.5f;
}
return median;
};
std::vector<float> rTemp = rVals, gTemp = gVals, bTemp = bVals;
return Color(getMedian(rTemp), getMedian(gTemp), getMedian(bTemp));
}
static float computeColorDistance(const Color& c1, const Color& c2) {
// 欧氏距离
float dr = c1.z - c2.z;
float dg = c1.y - c2.y;
float db = c1.x - c2.x;
return std::sqrt(dr*dr + dg*dg + db*db);
}
static Color computeRobustFaceColor(const FaceDataArr& faceDatas, float threshold = 2.0f) {
if (faceDatas.empty()) {
return Color::ZERO;
}
// 1. 提取所有颜色
std::vector<Color> colors;
colors.reserve(faceDatas.size());
for (const FaceData& fd : faceDatas) {
colors.push_back(fd.color);
}
// 2. 分别计算RGB三个通道的中位数
Color medianColor = computeMedianColor(colors);
// 3. 计算每个颜色到中位数的距离
std::vector<float> distances;
distances.reserve(colors.size());
for (const Color& color : colors) {
distances.push_back(computeColorDistance(color, medianColor));
}
// 4. 计算距离的中位数和MAD(绝对中位差)
std::vector<float> sortedDistances = distances;
std::sort(sortedDistances.begin(), sortedDistances.end());
float medianDistance = sortedDistances[sortedDistances.size() / 2];
// 计算绝对偏差
std::vector<float> absoluteDeviations;
absoluteDeviations.reserve(distances.size());
for (float d : distances) {
absoluteDeviations.push_back(std::abs(d - medianDistance));
}
std::sort(absoluteDeviations.begin(), absoluteDeviations.end());
float mad = absoluteDeviations[absoluteDeviations.size() / 2];
// 5. 确定阈值(使用3倍MAD规则)
float distanceThreshold = medianDistance + threshold * mad;
// 6. 计算筛选后的颜色均值
Color sumColor = Color::ZERO;
int validCount = 0;
for (size_t i = 0; i < colors.size(); ++i) {
if (distances[i] <= distanceThreshold) {
sumColor += colors[i];
validCount++;
} else {
// 可选:记录被剔除的颜色
// std::cout << "剔除异常颜色: " << colors[i] << ", 距离: " << distances[i] << std::endl;
}
}
if (validCount > 0) {
return sumColor / static_cast<float>(validCount);
} else {
// 如果没有有效颜色,返回中位数
return medianColor;
}
}
static Color computeFaceColorDifference(Mesh::Color& refColor, Color& faceColor) {
// printf("refColor:%f,%f,%f; faceColor:%d,%d,%d\n", refColor.r,refColor.g,refColor.b,faceColor[0],faceColor[1],faceColor[2]);
// 将refColor(0-255整数)转换为0-1范围的浮点数
Color refColorFloat(
refColor.r / 255.0f,
refColor.g / 255.0f,
refColor.b / 255.0f
);
// 计算差值(浮点数,可能有负值)
// 假设faceColor已经是0-1范围的浮点数
Color difference(
refColorFloat[0] - faceColor[0] / 255.0f,
refColorFloat[1] - faceColor[1] / 255.0f,
refColorFloat[2] - faceColor[2] / 255.0f
);
return difference;
}
static float computeFaceColorDifferenceF(Mesh::Color& refColor, Color& faceColor) {
// printf("refColor:%f,%f,%f; faceColor:%d,%d,%d\n", refColor.r,refColor.g,refColor.b,faceColor[0],faceColor[1],faceColor[2]);
// 将refColor(0-255整数)转换为0-1范围的浮点数
Color refColorFloat(
refColor.r / 255.0f,
refColor.g / 255.0f,
refColor.b / 255.0f
);
// 计算差值(浮点数,可能有负值)
// 假设faceColor已经是0-1范围的浮点数
Color difference(
refColorFloat[0] - faceColor[0] / 255.0f,
refColorFloat[1] - faceColor[1] / 255.0f,
refColorFloat[2] - faceColor[2] / 255.0f
);
int dr = difference[0];
int dg = difference[0];
int db = difference[0];
return std::sqrt(dr*dr + dg*dg + db*db);
}
static float computeMeshColorDifferenceF(const Mesh::Color& refColor1, const Mesh::Color& refColor2) {
// 将颜色转换为0-1范围的浮点数
Color refColorFloat1(
refColor1.r / 255.0f,
refColor1.g / 255.0f,
refColor1.b / 255.0f
);
Color refColorFloat2(
refColor2.r / 255.0f,
refColor2.g / 255.0f,
refColor2.b / 255.0f
);
// 计算差值(浮点数)
Color difference(
refColorFloat2[0] - refColorFloat1[0],
refColorFloat2[1] - refColorFloat1[1],
refColorFloat2[2] - refColorFloat1[2]
);
// 计算欧氏距离(浮点数)
float dr = difference[0];
float dg = difference[1]; // 修正:使用difference[1]
float db = difference[2]; // 修正:使用difference[2]
return std::sqrt(dr*dr + dg*dg + db*db);
}
static float computeColorDifferenceEnhanced(const Mesh::Color& c1, const Mesh::Color& c2) {
// 转换为0-1
float r1 = c1.r / 255.0f;
float g1 = c1.g / 255.0f;
float b1 = c1.b / 255.0f;
float r2 = c2.r / 255.0f;
float g2 = c2.g / 255.0f;
float b2 = c2.b / 255.0f;
// 计算亮度
float lum1 = 0.299f*r1 + 0.587f*g1 + 0.114f*b1;
float lum2 = 0.299f*r2 + 0.587f*g2 + 0.114f*b2;
// 亮度差异权重
float lumDiff = std::abs(lum1 - lum2);
// 色度差异
float chromaDiff = std::sqrt(
(r1 - r2)*(r1 - r2) +
(g1 - g2)*(g1 - g2) +
(b1 - b2)*(b1 - b2)
);
// 综合差异(亮度权重更高)
return 0.7f * lumDiff + 0.3f * chromaDiff;
}
static float computeColorDifferenceHSV2(Mesh::Color& c1, Mesh::Color& c2) {
// RGB转HSV
auto rgb2hsv = [](float r, float g, float b, float& h, float& s, float& v) {
float cmax = std::max({r, g, b});
float cmin = std::min({r, g, b});
float delta = cmax - cmin;
v = cmax;
if (cmax > 0) {
s = delta / cmax;
} else {
s = 0;
}
if (delta > 0) {
if (cmax == r) {
h = 60.0f * fmod((g - b) / delta, 6.0f);
} else if (cmax == g) {
h = 60.0f * (((b - r) / delta) + 2.0f);
} else if (cmax == b) {
h = 60.0f * (((r - g) / delta) + 4.0f);
}
if (h < 0) h += 360.0f;
} else {
h = 0;
}
};
// 转换为0-1
float r1 = c1.r / 255.0f, g1 = c1.g / 255.0f, b1 = c1.b / 255.0f;
float r2 = c2.r / 255.0f, g2 = c2.g / 255.0f, b2 = c2.b / 255.0f;
float h1, s1, v1, h2, s2, v2;
rgb2hsv(r1, g1, b1, h1, s1, v1);
rgb2hsv(r2, g2, b2, h2, s2, v2);
// 归一化H到0-1
h1 /= 360.0f; h2 /= 360.0f;
// 计算HSV空间距离(色相差异需考虑环形特性)
float dh = std::min(fabs(h1 - h2), 1.0f - fabs(h1 - h2));
float ds = fabs(s1 - s2);
float dv = fabs(v1 - v2);
// 加权距离(色相最重要,饱和度次之,亮度最不重要)
return std::sqrt(0.5f*dh*dh + 0.3f*ds*ds + 0.2f*dv*dv);
}
static float computeColorDifferenceHSV(MeshColor& c1, MeshColor& c2) {
// RGB转HSV
auto rgb2hsv = [](float r, float g, float b, float& h, float& s, float& v) {
float cmax = std::max({r, g, b});
float cmin = std::min({r, g, b});
float delta = cmax - cmin;
v = cmax;
if (cmax > 0) {
s = delta / cmax;
} else {
s = 0;
}
if (delta > 0) {
if (cmax == r) {
h = 60.0f * fmod((g - b) / delta, 6.0f);
} else if (cmax == g) {
h = 60.0f * (((b - r) / delta) + 2.0f);
} else if (cmax == b) {
h = 60.0f * (((r - g) / delta) + 4.0f);
}
if (h < 0) h += 360.0f;
} else {
h = 0;
}
};
// 转换为0-1
float r1 = c1.r / 255.0f, g1 = c1.g / 255.0f, b1 = c1.b / 255.0f;
float r2 = c2.r / 255.0f, g2 = c2.g / 255.0f, b2 = c2.b / 255.0f;
float h1, s1, v1, h2, s2, v2;
rgb2hsv(r1, g1, b1, h1, s1, v1);
rgb2hsv(r2, g2, b2, h2, s2, v2);
// 归一化H到0-1
h1 /= 360.0f; h2 /= 360.0f;
// 计算HSV空间距离(色相差异需考虑环形特性)
float dh = std::min(fabs(h1 - h2), 1.0f - fabs(h1 - h2));
float ds = fabs(s1 - s2);
float dv = fabs(v1 - v2);
// 加权距离(色相最重要,饱和度次之,亮度最不重要)
return std::sqrt(0.5f*dh*dh + 0.3f*ds*ds + 0.2f*dv*dv);
}
static float computeColorSimilarityWeighted(const Mesh::Color& c1, const Mesh::Color& c2) {
// 人眼对绿色最敏感,红色次之,蓝色最不敏感
const float weightR = 0.299f;
const float weightG = 0.587f;
const float weightB = 0.114f;
float dr = (c1.r - c2.r) / 255.0f;
float dg = (c1.g - c2.g) / 255.0f;
float db = (c1.b - c2.b) / 255.0f;
return std::sqrt(weightR*dr*dr + weightG*dg*dg + weightB*db*db);
}
static bool isTestFace(std::unordered_set<int>& face_test, int idxFace) {
if (face_test.find(idxFace) != face_test.end()) {
return true;
}
// if (idxFace==18919||idxFace==18925||idxFace==18926||
// idxFace==18932||idxFace==478724||idxFace==803492||
// idxFace==803496||idxFace==803497||idxFace==803507||idxFace==803508||
// idxFace==18921)
// {
// return true;
// }
return false;
}
//*/ //*/
/* /*
// 采用ITU-R BT.601标准系数,增加数值稳定性处理 // 采用ITU-R BT.601标准系数,增加数值稳定性处理
@ -673,6 +1007,9 @@ public:
Mesh::TexIndexArr& faceTexindices; // for each face, the texture-coordinates of the vertices Mesh::TexIndexArr& faceTexindices; // for each face, the texture-coordinates of the vertices
Mesh::Image8U3Arr& texturesDiffuse; // texture containing the diffuse color Mesh::Image8U3Arr& texturesDiffuse; // texture containing the diffuse color
Mesh::ColorArr& faceColorsGaussian;
Mesh::ColorArr faceOriginalColors;
// constant the entire time // constant the entire time
Mesh::VertexArr& vertices; Mesh::VertexArr& vertices;
Mesh::FaceArr& faces; Mesh::FaceArr& faces;
@ -726,14 +1063,23 @@ MeshTexture::MeshTexture(Scene& _scene, unsigned _nResolutionLevel, unsigned _nM
faces(_scene.mesh.faces), faces(_scene.mesh.faces),
images(_scene.images), images(_scene.images),
scene(_scene), scene(_scene),
alternativeTexture(nullptr) alternativeTexture(nullptr),
faceColorsGaussian(_scene.mesh.faceColors),
faceOriginalColors()
{ {
// 分配空间
faceOriginalColors.Resize(_scene.mesh.faceColors.size());
// 用默认值填充
for (size_t i = 0; i < faceOriginalColors.size(); ++i) {
faceOriginalColors[i] = Mesh::Color(0, 0, 0); // 或使用其他默认值
}
} }
MeshTexture::~MeshTexture() MeshTexture::~MeshTexture()
{ {
vertexFaces.Release(); vertexFaces.Release();
vertexBoundary.Release(); vertexBoundary.Release();
faceFaces.Release(); faceFaces.Release();
faceColorsGaussian.Release();
} }
void MeshTexture::ComputeFaceCurvatures() const { void MeshTexture::ComputeFaceCurvatures() const {
@ -786,9 +1132,21 @@ void MeshTexture::ListVertexFaces()
scene.mesh.ListIncidenteFaceFaces(); scene.mesh.ListIncidenteFaceFaces();
} }
static ColorComparisonFace* g_colorComparisonFace = nullptr;
static ColorComparisonPixel* g_colorComparisonPixel = nullptr;
// extract array of faces viewed by each image // extract array of faces viewed by each image
bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThreshold, int nIgnoreMaskLabel, const IIndexArr& _views, bool bUseVirtualFaces) bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThreshold, int nIgnoreMaskLabel, const IIndexArr& _views, bool bUseVirtualFaces)
{ {
#ifdef TEST
std::string outputDir = "/home/algo/Documents/openMVS/data/446442/color_comparison/";
g_colorComparisonFace = new ColorComparisonFace(outputDir);
std::map<IIndex, std::map<FIndex, std::vector<cv::Point>>> testFacePixelsByView;
g_colorComparisonPixel = new ColorComparisonPixel(outputDir);
#endif
// create faces octree // create faces octree
Mesh::Octree octree; Mesh::Octree octree;
Mesh::FacesInserter::CreateOctree(octree, scene.mesh); Mesh::FacesInserter::CreateOctree(octree, scene.mesh);
@ -1133,6 +1491,100 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
// if (!(scene.mesh.invalidFacesRelative.data.contains(idxFace) && scene.is_face_visible_relative(idxFace))) // if (!(scene.mesh.invalidFacesRelative.data.contains(idxFace) && scene.is_face_visible_relative(idxFace)))
// if (false) // if (false)
{ {
/*
Mesh::Color gaussianColor = faceColorsGaussian[idxFace];
FaceData& faceData = faceDatas.emplace_back();
faceData.idxView = idxView;
faceData.quality = imageGradMag(j,i);
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
// printf("gaussianColor:%d, %d, %d\n", gaussianColor[0], gaussianColor[1], gaussianColor[2]);
faceData.color = Color(gaussianColor[0] / 255.0f, gaussianColor[1] / 255.0f, gaussianColor[2] / 255.0f);
#endif
continue;
//*/
//*
if (idxFace<faceColorsGaussian.size())
{
Mesh::Color gaussianColor = faceColorsGaussian[idxFace];
// printf("idxFace(%d) gaussianColor:%d, %d, %d\n", idxFace, gaussianColor[0], gaussianColor[1], gaussianColor[2]);
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]);
MeshColor gaussianMeshColor(gaussianColor.r, gaussianColor.g, gaussianColor.b);
MeshColor originalMeshColor(originalColor.r, originalColor.g, originalColor.b);
float colorDistance = computeColorDifferenceHSV(gaussianMeshColor, originalMeshColor);
// printf("colorDistance=%f\n",colorDistance);
float threshold = 0.31f; // 基础阈值
// printf("colorDistance=%f\n",colorDistance);
if (colorDistance > threshold)
{
// FaceData& faceData = faceDatas.emplace_back();
// faceData.idxView = idxView;
// faceData.quality = 0;
// faceData.bInvalidFacesRelative = true;
// printf("faceData.quality=%f\n", faceData.quality);
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
// faceData.color = imageData.image(j,i);
#endif
// continue;
}
//*/
#ifdef TEST
bool bFilter = (colorDistance > threshold) ? true : false;
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);
}
if (isTestFace(scene.face_test, idxFace))
{
// printf("Test face(%d) Color: gaussian(%d,%d,%d), image(%s) color(%d,%d,%d), colorDistance(%f), threshold(%f), filter=%b\n",idxFace, gaussianColor.r, gaussianColor.g, gaussianColor.b,
// filename.c_str(), originalColor.r, originalColor.g, originalColor.b, colorDistance, threshold, bFilter);
// 创建MeshColor对象
MeshColor gaussianMeshColor(gaussianColor.r, gaussianColor.g, gaussianColor.b);
MeshColor originalMeshColor(originalColor.r, originalColor.g, originalColor.b);
// 添加到批处理器
if (g_colorComparisonPixel) {
g_colorComparisonPixel->addColorInfo(idxFace,
gaussianMeshColor,
originalMeshColor,
colorDistance, threshold, filename);
}
// 添加到批处理器
if (g_colorComparisonFace) {
testFacePixelsByView[idxView][idxFace].push_back(cv::Point(i, j));
printf("testFacePixels imageView(%s), idxFace(%d) push_back (%d, %d)\n", filename.c_str(), idxFace, i, j);
}
}
#endif
}
if (depthMap(j,i)>999.0f) if (depthMap(j,i)>999.0f)
{ {
/* /*
@ -1145,10 +1597,12 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
// printf("faceData.quality=%f\n", faceData.quality); // printf("faceData.quality=%f\n", faceData.quality);
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
faceData.color = imageData.image(j,i); faceData.color = imageData.image(j,i);
// faceData.color = Point3f::ZERO;
#endif #endif
continue; continue;
*/ //*/
/*
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
uint32_t& area = areas[idxFace]; uint32_t& area = areas[idxFace];
if (area++ == 0) { if (area++ == 0) {
@ -1175,6 +1629,7 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
faceData.bInvalidFacesRelative = true; faceData.bInvalidFacesRelative = true;
} }
continue; continue;
//*/
} }
} }
@ -1210,6 +1665,160 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
} }
} }
} }
#ifdef TEST
auto itView = testFacePixelsByView.find(idxView);
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<cv::Point>& pixels = faceEntry.second;
printf(" 处理面 %d: 有 %zu 个像素\n", idxFace, pixels.size());
// 检查像素是否为空
if (pixels.empty()) {
printf(" 面 %d 没有像素,跳过\n", idxFace);
continue;
}
// 计算面片区域的包围盒
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) {
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<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 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::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;
}
printf(" 提取图像区域成功: %dx%d\n", imageRegion.cols, imageRegion.rows);
// 计算图像区域的平均颜色
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);
}
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 (g_colorComparisonFace) {
g_colorComparisonFace->addColorInfo(idxFace,
gaussianMeshColor,
imageRegion,
colorDistance, threshold, filename);
printf(" ✅ 面 %d 已添加到批处理器\n", idxFace);
}
}
printf("=== 视图 %d 处理完成 ===\n\n", idxView);
} else {
printf("视图 %d: 没有测试面\n", idxView);
}
#endif
// adjust face quality with camera angle relative to face normal // adjust face quality with camera angle relative to face normal
// tries to increase chances of a camera with perpendicular view on the surface (smoothened normals) to be selected // tries to increase chances of a camera with perpendicular view on the surface (smoothened normals) to be selected
FOREACH(idxFace, facesDatas) { FOREACH(idxFace, facesDatas) {
@ -1249,6 +1858,25 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
FaceOutlierDetection(faceDatas, fOutlierThreshold); FaceOutlierDetection(faceDatas, fOutlierThreshold);
} }
#endif #endif
#ifdef TEST
if (g_colorComparisonFace) {
// 创建实际图像区域对比图
printf("创建实际图像区域对比图,包含 %d 个face,%d 条记录\n",
g_colorComparisonFace->getTotalFaces(), g_colorComparisonFace->getTotalRecords());
g_colorComparisonFace->createBatchComparison(10, 500);
delete g_colorComparisonFace;
g_colorComparisonFace = nullptr;
}
if (g_colorComparisonPixel) {
g_colorComparisonPixel->createBatchComparison(10, 500); // 每行4个,最多20个
delete g_colorComparisonPixel;
g_colorComparisonPixel = nullptr;
}
#endif
return true; return true;
} }
@ -1690,7 +2318,7 @@ std::unordered_set<FIndex> MeshTexture::PerformLocalDepthConsistencyCheck(DepthM
// 第三遍:清除不一致区域的深度值(现在可以安全修改原始depthMap) // 第三遍:清除不一致区域的深度值(现在可以安全修改原始depthMap)
for (int r = 0; r < depthMap.rows; ++r) { for (int r = 0; r < depthMap.rows; ++r) {
for (int c = 0; c < depthMap.cols; ++c) { for (int c = 0; c < depthMap.cols; ++c) {
if (consistencyMask(r, c) == 255) if (consistencyMask(r, c) == 255)
{ {
const FIndex idxFace = faceMap(r, c); const FIndex idxFace = faceMap(r, c);
if (idxFace == NO_ID || idxFace >= mesh.faces.size()) { if (idxFace == NO_ID || idxFace >= mesh.faces.size()) {
@ -4215,9 +4843,12 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
for (const FaceData& fd : faceDatas) { for (const FaceData& fd : faceDatas) {
sumColor += fd.color; sumColor += fd.color;
} }
faceColors[idxFace] = sumColor / faceDatas.size(); // faceColors[idxFace] = sumColor / faceDatas.size();
faceColors[idxFace] = computeRobustFaceColor(faceDatas, 10.0f);
} }
do { do {
const FIndex startPos = RAND() % remainingFaces.size(); const FIndex startPos = RAND() % remainingFaces.size();
const FIndex virtualFaceCenterFaceID = remainingFaces[startPos]; const FIndex virtualFaceCenterFaceID = remainingFaces[startPos];
@ -4359,8 +4990,8 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
if (angleDeg <= 45.0f) if (angleDeg <= 45.0f)
{ {
// filteredCams.push_back(idxView); filteredCams.push_back(idxView);
//* /*
float brightnessScore = CalculateBrightnessScore(imageData); // 亮度评分函数 float brightnessScore = CalculateBrightnessScore(imageData); // 亮度评分函数
float angleScore = 1.0f - (angleDeg / 45.0f); float angleScore = 1.0f - (angleDeg / 45.0f);
float qualityScore = 0.0f; float qualityScore = 0.0f;
@ -4382,9 +5013,9 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
} }
else else
{ {
// filteredCams.push_back(idxView); filteredCams.push_back(idxView);
//* /*
float brightnessScore = CalculateBrightnessScore(imageData); // 亮度评分函数 float brightnessScore = CalculateBrightnessScore(imageData); // 亮度评分函数
float angleScore = 1.0f - (angleDeg / 45.0f); float angleScore = 1.0f - (angleDeg / 45.0f);
float qualityScore = 0.0f; float qualityScore = 0.0f;
@ -9234,11 +9865,41 @@ void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLevel
for (const FIndex idxFace: texturePatch.faces) { for (const FIndex idxFace: texturePatch.faces) {
const Face& face = faces[idxFace]; const Face& face = faces[idxFace];
TexCoord* texcoords = faceTexcoords.data()+idxFace*3; TexCoord* texcoords = faceTexcoords.data()+idxFace*3;
TexCoord centroid(0,0);
for (int i=0; i<3; ++i) { for (int i=0; i<3; ++i) {
texcoords[i] = imageData.camera.ProjectPointP(vertices[face[i]]); texcoords[i] = imageData.camera.ProjectPointP(vertices[face[i]]);
centroid += texcoords[i];
ASSERT(imageData.image.isInsideWithBorder(texcoords[i], border)); ASSERT(imageData.image.isInsideWithBorder(texcoords[i], border));
aabb.InsertFull(texcoords[i]); aabb.InsertFull(texcoords[i]);
} }
centroid /= 3.0f;
// 获取重心处的颜色
int cx = static_cast<int>(centroid.x + 0.5f);
int cy = static_cast<int>(centroid.y + 0.5f);
// 边界检查
cx = std::max(0, std::min(cx, imageData.image.cols-1));
cy = std::max(0, std::min(cy, imageData.image.rows-1));
#ifdef TEST
// 获取颜色
const Pixel8U& pixel = imageData.image(cy, cx);
faceOriginalColors[idxFace] = Mesh::Color(pixel.r, pixel.g, pixel.b);
// if (idxFace==18919||idxFace==18925||idxFace==18926||
// idxFace==18932||idxFace==478724||idxFace==803492||
// idxFace==803496||idxFace==803497||idxFace==803507||idxFace==803508||
// idxFace==18921)
if (isTestFace(scene.face_test, idxFace))
{
Mesh::Color originalColor= faceOriginalColors[idxFace];
printf("idxFace(%d) record originalColor:%d, %d, %d\n", idxFace, originalColor[0], originalColor[1], originalColor[2]);
}
#endif
} }
// compute relative texture coordinates // compute relative texture coordinates
ASSERT(imageData.image.isInside(Point2f(aabb.ptMin))); ASSERT(imageData.image.isInside(Point2f(aabb.ptMin)));
@ -9465,6 +10126,77 @@ void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLevel
x = 1; y = 0; x = 1; y = 0;
} }
// 检查并处理目标面
for (const FIndex idxFace: texturePatch.faces) {
if (isTestFace(scene.face_test, idxFace))
// if (idxFace==78133)
{
// 找到这个面对应于纹理块中的区域
TexCoord* texcoords = faceTexcoords.data() + idxFace * 3;
// 计算在纹理块内的局部坐标
TexCoord localCoords[3];
for (int v = 0; v < 3; ++v) {
localCoords[v] = TexCoord(
texcoords[v][x],
texcoords[v][y]
);
}
// 转换为相对于纹理块的坐标
TexCoord offset(texturePatch.rect.tl());
for (int v = 0; v < 3; ++v) {
localCoords[v] += offset;
}
// 在patch中填充三角形为黑色
cv::Point2f triangle[3];
for (int v = 0; v < 3; ++v) {
triangle[v] = cv::Point2f(
localCoords[v].x - texturePatch.rect.x,
localCoords[v].y - texturePatch.rect.y
);
}
const cv::Point2f* ppt[1] = { triangle };
int npt[] = { 3 };
std::vector<cv::Point> trianglePoints(3);
for (int v = 0; v < 3; ++v) {
trianglePoints[v] = cv::Point(
static_cast<int>(localCoords[v].x - texturePatch.rect.x + 0.5f),
static_cast<int>(localCoords[v].y - texturePatch.rect.y + 0.5f)
);
}
std::vector<std::vector<cv::Point>> contours = {trianglePoints};
// cv::fillPoly(patch, contours, cv::Scalar(0, 0, 0));
Mesh::Color gaussianColor = faceColorsGaussian[idxFace];
// printf("idxFace(%d) gaussianColor:%d, %d, %d\n", idxFace, gaussianColor[0], gaussianColor[1], gaussianColor[2]);
Mesh::Color originalColor= faceOriginalColors[idxFace];
// printf("idxFace(%d) originalColor:%d, %d, %d\n", idxFace, originalColor[0], originalColor[1], originalColor[2]);
MeshColor gaussianMeshColor(gaussianColor.r, gaussianColor.g, gaussianColor.b);
MeshColor originalMeshColor(originalColor.r, originalColor.g, originalColor.b);
// float colorDistance = computeColorSimilarityWeighted(gaussianColor, originalColor);
// float colorDistance = computeColorDifferenceEnhanced(gaussianColor, originalColor);
float colorDistance = computeColorDifferenceHSV(gaussianMeshColor, originalMeshColor);
// printf("colorDistance=%f\n",colorDistance);
float threshold = 0.12f; // 基础阈值
// 考虑面的面积:小面对颜色变化更敏感
// float faceArea = faces[idxFace].area;
// if (faceArea < 0.001f) { // 非常小的面
// threshold *= 0.8f; // 更严格的阈值
// }
// if (colorDistance>threshold)
// cv::fillPoly(patch, contours, cv::Scalar(gaussianColor[0], gaussianColor[1], gaussianColor[2]));
}
}
patch.copyTo(texturesDiffuse[idxTexture](rect)); patch.copyTo(texturesDiffuse[idxTexture](rect));
} }
else else
@ -10406,6 +11138,26 @@ bool Scene::LoadVisibleFacesData(std::map<std::string, std::unordered_set<int>>&
return true; return true;
} }
// 从文件加载遮挡数据
bool Scene::LoadTestFacesData(std::unordered_set<int>& face_test, std::string& basePath) {
printf("LoadTestFacesData %s\n", basePath.c_str());
std::ifstream testFacesFile(basePath + "_test_faces.txt");
if (!testFacesFile.is_open()) {
return false;
}
std::string line;
while (std::getline(testFacesFile, line)) {
int face_index = std::stoi(line);
face_test.insert(face_index);
printf("insert face_index=%d\n", face_index);
}
testFacesFile.close();
return true;
}
// texture mesh // texture mesh
// - minCommonCameras: generate texture patches using virtual faces composed of coplanar triangles sharing at least this number of views (0 - disabled, 3 - good value) // - minCommonCameras: generate texture patches using virtual faces composed of coplanar triangles sharing at least this number of views (0 - disabled, 3 - good value)
// - fSharpnessWeight: sharpness weight to be applied on the texture (0 - disabled, 0.5 - good value) // - fSharpnessWeight: sharpness weight to be applied on the texture (0 - disabled, 0.5 - good value)
@ -10413,7 +11165,7 @@ bool Scene::LoadVisibleFacesData(std::map<std::string, std::unordered_set<int>>&
bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsigned minCommonCameras, float fOutlierThreshold, float fRatioDataSmoothness, bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsigned minCommonCameras, float fOutlierThreshold, float fRatioDataSmoothness,
bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight,
int nIgnoreMaskLabel, int maxTextureSize, const IIndexArr& views, const SEACAVE::String& baseFileName, bool bOriginFaceview, int nIgnoreMaskLabel, int maxTextureSize, const IIndexArr& views, const SEACAVE::String& baseFileName, bool bOriginFaceview,
const std::string& inputFileName, const std::string& meshFileName) const std::string& inputFileName, const std::string& meshFileName, bool bExistVertexColor)
{ {
if (!bOriginFaceview) if (!bOriginFaceview)
{ {
@ -10428,6 +11180,14 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
mesh.ComputeNormalFaces(); // 计算面法向量 mesh.ComputeNormalFaces(); // 计算面法向量
} }
if (bExistVertexColor)
{
printf("bExistVertexColor true\n");
if (mesh.faceColors.empty()) {
mesh.ComputeColorFaces(); // 计算面法向量
}
}
// 确保顶点边界信息已计算 // 确保顶点边界信息已计算
if (mesh.vertexBoundary.empty()) { if (mesh.vertexBoundary.empty()) {
mesh.ListBoundaryVertices(); // 计算边界顶点 mesh.ListBoundaryVertices(); // 计算边界顶点
@ -10481,7 +11241,6 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
} }
printf("id=%s\n", id.c_str()); printf("id=%s\n", id.c_str());
#ifdef MASK_FACE_OCCLUSION
//* //*
fs::path p(baseFileName.c_str()); fs::path p(baseFileName.c_str());
// 2. 获取父路径 (e.g., /path/to/data/scene) // 2. 获取父路径 (e.g., /path/to/data/scene)
@ -10499,6 +11258,7 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
basePath = baseFileName.substr(0, secondLastSlash + 1); basePath = baseFileName.substr(0, secondLastSlash + 1);
/*/ /*/
#ifdef MASK_FACE_OCCLUSION
// printf("basePath=%s\n", basePath.c_str()); // printf("basePath=%s\n", basePath.c_str());
if (!LoadVisibleFacesData(visible_faces_map, face_visible_relative, edge_faces_map, delete_edge_faces_map, basePath)) if (!LoadVisibleFacesData(visible_faces_map, face_visible_relative, edge_faces_map, delete_edge_faces_map, basePath))
@ -10507,6 +11267,13 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
} }
#endif #endif
#ifdef TEST
if (!LoadTestFacesData(face_test, basePath))
{
printf("LoadVisibleFacesData error\n");
}
#endif
// assign the best view to each face // assign the best view to each face
{ {
TD_TIMER_STARTD(); TD_TIMER_STARTD();

Loading…
Cancel
Save