Compare commits

...

1 Commits

Author SHA1 Message Date
hesuicong e8e84643f6 添加视图比对 6 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. 789
      libs/MVS/SceneTexture.cpp

6
apps/TextureMesh/TextureMesh.cpp

@ -1098,7 +1098,9 @@ int main(int argc, LPCTSTR* argv) @@ -1098,7 +1098,9 @@ int main(int argc, LPCTSTR* argv)
return EXIT_FAILURE;
printf("Now\n");
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");
return EXIT_FAILURE;
}
@ -1134,7 +1136,7 @@ int main(int argc, LPCTSTR* argv) @@ -1134,7 +1136,7 @@ int main(int argc, LPCTSTR* argv)
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::fSharpnessWeight, OPT::nIgnoreMaskLabel, OPT::nMaxTextureSize, views, baseFileName, OPT::bOriginFaceview,
OPT::strInputFileName, OPT::strMeshFileName))
OPT::strInputFileName, OPT::strMeshFileName, *pBExistVertexColor))
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());

10
libs/Common/Util.inl

@ -579,6 +579,16 @@ inline TPoint3<TYPE> ComputeTriangleNormal(const TPoint3<TYPE>& v0, const TPoint @@ -579,6 +579,16 @@ inline TPoint3<TYPE> ComputeTriangleNormal(const TPoint3<TYPE>& v0, const TPoint
} // 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
template <typename TYPE>
TYPE ComputeTriangleAreaSqLen(TYPE lena, TYPE lenb, TYPE lenc) {

428
libs/MVS/ColorComparisonFace.cpp

@ -0,0 +1,428 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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() @@ -350,6 +350,16 @@ void Mesh::ComputeNormalFaces()
#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
#if 1
// computes the vertex normal as the area weighted face normals average
@ -1096,18 +1106,20 @@ namespace BasicPLY { @@ -1096,18 +1106,20 @@ namespace BasicPLY {
struct Vertex {
Mesh::Vertex v;
Mesh::Normal n;
Mesh::Color color;
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]);
const size_t nMaxProps(SizeOfArray(props));
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)
continue;
ply.setup_property(props[p]);
switch (p) {
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 { @@ -1125,6 +1137,15 @@ namespace BasicPLY {
}
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[] = {
{"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},
@ -1133,6 +1154,7 @@ namespace BasicPLY { @@ -1133,6 +1154,7 @@ namespace BasicPLY {
{"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}
};
*/
// list of property information for a face
struct Face {
struct FaceIndices {
@ -1191,7 +1213,7 @@ namespace BasicPLY { @@ -1191,7 +1213,7 @@ namespace BasicPLY {
} // namespace MeshInternal
// import the mesh from the given file
bool Mesh::Load(const String& fileName)
bool Mesh::Load(const String& fileName, bool *pBExistVertexColor)
{
TD_TIMER_STARTD();
const String ext(Util::getFileExt(fileName).ToLower());
@ -1202,14 +1224,14 @@ bool Mesh::Load(const String& fileName) @@ -1202,14 +1224,14 @@ bool Mesh::Load(const String& fileName)
if (ext == _T(".gltf") || ext == _T(".glb"))
ret = LoadGLTF(fileName, ext == _T(".glb"));
else
ret = LoadPLY(fileName);
ret = LoadPLY(fileName, pBExistVertexColor);
if (!ret)
return false;
DEBUG_EXTRA("Mesh loaded: %u vertices, %u faces (%s)", vertices.size(), faces.size(), TD_TIMER_GET_FMT().c_str());
return true;
}
// import the mesh as a PLY file
bool Mesh::LoadPLY(const String& fileName)
bool Mesh::LoadPLY(const String& fileName, bool *pBExistVertexColor)
{
ASSERT(!fileName.empty());
Release();
@ -1244,16 +1266,23 @@ bool Mesh::LoadPLY(const String& fileName) @@ -1244,16 +1266,23 @@ bool Mesh::LoadPLY(const String& fileName)
LPCSTR elem_name = ply.setup_element_read(i, &elem_count);
if (PLY::equal_strings(BasicPLY::elem_names[0], elem_name)) {
ASSERT(vertices.size() == (VIndex)elem_count);
BasicPLY::Vertex::InitLoadProps(ply, elem_count, vertices, vertexNormals);
if (vertexNormals.empty()) {
// BasicPLY::Vertex::InitLoadProps(ply, elem_count, vertices, vertexNormals);
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)
ply.get_element(&vert);
} else {
// printf("Not vertexColors.empty, elem_count=%u\n", elem_count);
*pBExistVertexColor = true;
BasicPLY::Vertex vertex;
for (int v=0; v<elem_count; ++v) {
ply.get_element(&vertex);
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

16
libs/MVS/Mesh.h

@ -60,6 +60,9 @@ public: @@ -60,6 +60,9 @@ public:
typedef SEACAVE::cList<Vertex,const Vertex&,0,8192,VIndex> VertexArr;
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<FIndex,FIndex,0,8,FIndex> FaceIdxArr;
@ -154,6 +157,9 @@ public: @@ -154,6 +157,9 @@ public:
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)
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)
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)
@ -194,6 +200,8 @@ public: @@ -194,6 +200,8 @@ public:
void ComputeNormalFaces();
void ComputeNormalVertices();
void ComputeColorFaces();
void SmoothNormalFaces(float fMaxGradient=25.f, float fOriginalWeight=0.5f, unsigned nIterations=3);
void GetEdgeFaces(VIndex, VIndex, FaceIdxArr&) const;
@ -238,6 +246,10 @@ public: @@ -238,6 +246,10 @@ public:
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;
Vertex ComputeCentroid(FIndex) const;
@ -262,7 +274,7 @@ public: @@ -262,7 +274,7 @@ public:
bool TransferTexture(Mesh& mesh, const FaceIdxArr& faceSubsetIndices={}, unsigned borderSize=3, unsigned textureSize=4096);
// 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 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);
@ -272,7 +284,7 @@ public: @@ -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]; }
protected:
bool LoadPLY(const String& fileName);
bool LoadPLY(const String& fileName, bool *pBExistVertexColor);
bool LoadOBJ(const String& fileName);
bool LoadGLTF(const String& fileName, bool bBinary=true);

4
libs/MVS/Scene.h

@ -66,6 +66,7 @@ public: @@ -66,6 +66,7 @@ public:
std::map<std::string, std::unordered_set<int>> 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_test;
public:
inline Scene(unsigned _nMaxThreads=0)
@ -164,12 +165,13 @@ public: @@ -164,12 +165,13 @@ public:
std::map<std::string, std::unordered_set<int>>& edge_faces_map,
std::map<std::string, std::unordered_set<int>>& delete_edge_faces_map,
std::string& basePath);
bool LoadTestFacesData(std::unordered_set<int>& face_test, std::string& basePath);
// Mesh texturing
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),
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);
bool is_face_visible(const std::string& image_name, int face_index);

789
libs/MVS/SceneTexture.cpp

@ -43,6 +43,8 @@ @@ -43,6 +43,8 @@
#include "cuda/MeshTextureCUDA.h"
#include <sstream>
#include <filesystem>
#include "ColorComparisonFace.h"
#include "ColorComparisonPixel.h"
namespace fs = std::filesystem;
@ -76,6 +78,8 @@ using namespace MVS; @@ -76,6 +78,8 @@ using namespace MVS;
#define TEXOPT_INFERENCE_TRWS 2
#define TEXOPT_INFERENCE TEXOPT_INFERENCE_LBP
#define MASK_FACE_OCCLUSION
// #define TEST
// #define USE_CUDA
// inference algorithm
@ -590,6 +594,336 @@ public: @@ -590,6 +594,336 @@ public:
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标准系数,增加数值稳定性处理
@ -673,6 +1007,9 @@ public: @@ -673,6 +1007,9 @@ public:
Mesh::TexIndexArr& faceTexindices; // for each face, the texture-coordinates of the vertices
Mesh::Image8U3Arr& texturesDiffuse; // texture containing the diffuse color
Mesh::ColorArr& faceColorsGaussian;
Mesh::ColorArr faceOriginalColors;
// constant the entire time
Mesh::VertexArr& vertices;
Mesh::FaceArr& faces;
@ -726,14 +1063,23 @@ MeshTexture::MeshTexture(Scene& _scene, unsigned _nResolutionLevel, unsigned _nM @@ -726,14 +1063,23 @@ MeshTexture::MeshTexture(Scene& _scene, unsigned _nResolutionLevel, unsigned _nM
faces(_scene.mesh.faces),
images(_scene.images),
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()
{
vertexFaces.Release();
vertexBoundary.Release();
faceFaces.Release();
faceColorsGaussian.Release();
}
void MeshTexture::ComputeFaceCurvatures() const {
@ -786,9 +1132,21 @@ void MeshTexture::ListVertexFaces() @@ -786,9 +1132,21 @@ void MeshTexture::ListVertexFaces()
scene.mesh.ListIncidenteFaceFaces();
}
static ColorComparisonFace* g_colorComparisonFace = nullptr;
static ColorComparisonPixel* g_colorComparisonPixel = nullptr;
// extract array of faces viewed by each image
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
Mesh::Octree octree;
Mesh::FacesInserter::CreateOctree(octree, scene.mesh);
@ -1133,6 +1491,100 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr @@ -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 (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)
{
/*
@ -1145,10 +1597,12 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr @@ -1145,10 +1597,12 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
// printf("faceData.quality=%f\n", faceData.quality);
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
faceData.color = imageData.image(j,i);
// faceData.color = Point3f::ZERO;
#endif
continue;
*/
//*/
/*
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
uint32_t& area = areas[idxFace];
if (area++ == 0) {
@ -1175,6 +1629,7 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr @@ -1175,6 +1629,7 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
faceData.bInvalidFacesRelative = true;
}
continue;
//*/
}
}
@ -1210,6 +1665,160 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr @@ -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
// tries to increase chances of a camera with perpendicular view on the surface (smoothened normals) to be selected
FOREACH(idxFace, facesDatas) {
@ -1249,6 +1858,25 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr @@ -1249,6 +1858,25 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr
FaceOutlierDetection(faceDatas, fOutlierThreshold);
}
#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;
}
@ -1690,7 +2318,7 @@ std::unordered_set<FIndex> MeshTexture::PerformLocalDepthConsistencyCheck(DepthM @@ -1690,7 +2318,7 @@ std::unordered_set<FIndex> MeshTexture::PerformLocalDepthConsistencyCheck(DepthM
// 第三遍:清除不一致区域的深度值(现在可以安全修改原始depthMap)
for (int r = 0; r < depthMap.rows; ++r) {
for (int c = 0; c < depthMap.cols; ++c) {
if (consistencyMask(r, c) == 255)
if (consistencyMask(r, c) == 255)
{
const FIndex idxFace = faceMap(r, c);
if (idxFace == NO_ID || idxFace >= mesh.faces.size()) {
@ -4215,9 +4843,12 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView @@ -4215,9 +4843,12 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
for (const FaceData& fd : faceDatas) {
sumColor += fd.color;
}
faceColors[idxFace] = sumColor / faceDatas.size();
// faceColors[idxFace] = sumColor / faceDatas.size();
faceColors[idxFace] = computeRobustFaceColor(faceDatas, 10.0f);
}
do {
const FIndex startPos = RAND() % remainingFaces.size();
const FIndex virtualFaceCenterFaceID = remainingFaces[startPos];
@ -4359,8 +4990,8 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView @@ -4359,8 +4990,8 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
if (angleDeg <= 45.0f)
{
// filteredCams.push_back(idxView);
//*
filteredCams.push_back(idxView);
/*
float brightnessScore = CalculateBrightnessScore(imageData); // 亮度评分函数
float angleScore = 1.0f - (angleDeg / 45.0f);
float qualityScore = 0.0f;
@ -4382,9 +5013,9 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView @@ -4382,9 +5013,9 @@ bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataView
}
else
{
// filteredCams.push_back(idxView);
filteredCams.push_back(idxView);
//*
/*
float brightnessScore = CalculateBrightnessScore(imageData); // 亮度评分函数
float angleScore = 1.0f - (angleDeg / 45.0f);
float qualityScore = 0.0f;
@ -9234,11 +9865,41 @@ void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLevel @@ -9234,11 +9865,41 @@ void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLevel
for (const FIndex idxFace: texturePatch.faces) {
const Face& face = faces[idxFace];
TexCoord* texcoords = faceTexcoords.data()+idxFace*3;
TexCoord centroid(0,0);
for (int i=0; i<3; ++i) {
texcoords[i] = imageData.camera.ProjectPointP(vertices[face[i]]);
centroid += texcoords[i];
ASSERT(imageData.image.isInsideWithBorder(texcoords[i], border));
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
ASSERT(imageData.image.isInside(Point2f(aabb.ptMin)));
@ -9464,7 +10125,78 @@ void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLevel @@ -9464,7 +10125,78 @@ void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLevel
patch = patch.t();
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));
}
else
@ -10406,6 +11138,26 @@ bool Scene::LoadVisibleFacesData(std::map<std::string, std::unordered_set<int>>& @@ -10406,6 +11138,26 @@ bool Scene::LoadVisibleFacesData(std::map<std::string, std::unordered_set<int>>&
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
// - 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)
@ -10413,7 +11165,7 @@ bool Scene::LoadVisibleFacesData(std::map<std::string, std::unordered_set<int>>& @@ -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 bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight,
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)
{
@ -10427,6 +11179,14 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi @@ -10427,6 +11179,14 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
if (mesh.faceNormals.empty()) {
mesh.ComputeNormalFaces(); // 计算面法向量
}
if (bExistVertexColor)
{
printf("bExistVertexColor true\n");
if (mesh.faceColors.empty()) {
mesh.ComputeColorFaces(); // 计算面法向量
}
}
// 确保顶点边界信息已计算
if (mesh.vertexBoundary.empty()) {
@ -10481,7 +11241,6 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi @@ -10481,7 +11241,6 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
}
printf("id=%s\n", id.c_str());
#ifdef MASK_FACE_OCCLUSION
//*
fs::path p(baseFileName.c_str());
// 2. 获取父路径 (e.g., /path/to/data/scene)
@ -10499,6 +11258,7 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi @@ -10499,6 +11258,7 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
basePath = baseFileName.substr(0, secondLastSlash + 1);
/*/
#ifdef MASK_FACE_OCCLUSION
// printf("basePath=%s\n", basePath.c_str());
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 @@ -10507,6 +11267,13 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
}
#endif
#ifdef TEST
if (!LoadTestFacesData(face_test, basePath))
{
printf("LoadVisibleFacesData error\n");
}
#endif
// assign the best view to each face
{
TD_TIMER_STARTD();

Loading…
Cancel
Save