|
|
|
@ -9781,7 +9781,7 @@ Pixel8U SampleImageBilinear(const Image8U3& image, const Point2f& point) { |
|
|
|
return result; |
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void FillTextureGaps(Image8U3& textureAtlas, const Mesh::TexCoordArr& faceTexcoords, |
|
|
|
void FillTextureGaps2(Image8U3& textureAtlas, const Mesh::TexCoordArr& faceTexcoords, |
|
|
|
FIndex nFaces, const MeshTexture::LabelArr& faceLabels, |
|
|
|
FIndex nFaces, const MeshTexture::LabelArr& faceLabels, |
|
|
|
const IIndexArr& views, int textureSize, Pixel8U colEmpty) |
|
|
|
const IIndexArr& views, int textureSize, Pixel8U colEmpty) |
|
|
|
{ |
|
|
|
{ |
|
|
|
@ -9926,6 +9926,213 @@ void FillTextureGaps(Image8U3& textureAtlas, const Mesh::TexCoordArr& faceTexcoo |
|
|
|
inpaintResult.copyTo(textureAtlas); |
|
|
|
inpaintResult.copyTo(textureAtlas); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("纹理间隙填充完成"); |
|
|
|
|
|
|
|
}// 修改TextureGrid结构体,在构造函数中传入纹理大小
|
|
|
|
|
|
|
|
struct TextureGrid { |
|
|
|
|
|
|
|
std::vector<std::vector<FIndex>> grid; |
|
|
|
|
|
|
|
float cellSize; |
|
|
|
|
|
|
|
int gridSize; |
|
|
|
|
|
|
|
int textureSize; // 添加纹理大小成员变量
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TextureGrid(int _textureSize, int cellCount) : textureSize(_textureSize) { |
|
|
|
|
|
|
|
cellSize = (float)textureSize / cellCount; |
|
|
|
|
|
|
|
gridSize = cellCount; |
|
|
|
|
|
|
|
grid.resize(gridSize * gridSize); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void AddTriangle(FIndex faceIdx, const TexCoord* uvCoords, int _textureSize) { // 添加纹理大小参数
|
|
|
|
|
|
|
|
int minX = _textureSize, maxX = 0; |
|
|
|
|
|
|
|
int minY = _textureSize, maxY = 0; |
|
|
|
|
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
|
|
|
|
int px = std::max(0, std::min(_textureSize - 1, (int)(uvCoords[i].x * _textureSize))); |
|
|
|
|
|
|
|
int py = std::max(0, std::min(_textureSize - 1, (int)(uvCoords[i].y * _textureSize))); |
|
|
|
|
|
|
|
minX = std::min(minX, px); |
|
|
|
|
|
|
|
maxX = std::max(maxX, px); |
|
|
|
|
|
|
|
minY = std::min(minY, py); |
|
|
|
|
|
|
|
maxY = std::max(maxY, py); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int cellMinX = std::max(0, (int)(minX / cellSize)); |
|
|
|
|
|
|
|
int cellMaxX = std::min(gridSize-1, (int)(maxX / cellSize)); |
|
|
|
|
|
|
|
int cellMinY = std::max(0, (int)(minY / cellSize)); |
|
|
|
|
|
|
|
int cellMaxY = std::min(gridSize-1, (int)(maxY / cellSize)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int cy = cellMinY; cy <= cellMaxY; ++cy) { |
|
|
|
|
|
|
|
for (int cx = cellMinX; cx <= cellMaxX; ++cx) { |
|
|
|
|
|
|
|
grid[cy * gridSize + cx].push_back(faceIdx); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const std::vector<FIndex>& GetTrianglesInCell(int x, int y) const { |
|
|
|
|
|
|
|
return grid[y * gridSize + x]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void GenerateDistanceFieldFast(cv::Mat& distanceField, cv::Mat& nearestFaceIdx, |
|
|
|
|
|
|
|
const std::vector<cv::Point>& seedPoints, |
|
|
|
|
|
|
|
int textureSize) { |
|
|
|
|
|
|
|
// 使用跳点传播算法
|
|
|
|
|
|
|
|
distanceField = cv::Mat::zeros(textureSize, textureSize, CV_32FC1); |
|
|
|
|
|
|
|
nearestFaceIdx = cv::Mat::zeros(textureSize, textureSize, CV_32SC1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化距离场
|
|
|
|
|
|
|
|
distanceField.setTo(std::numeric_limits<float>::max()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 标记种子点
|
|
|
|
|
|
|
|
for (const auto& pt : seedPoints) { |
|
|
|
|
|
|
|
distanceField.at<float>(pt) = 0; |
|
|
|
|
|
|
|
nearestFaceIdx.at<int>(pt) = pt.y * textureSize + pt.x; // 存储自身索引
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 跳点传播
|
|
|
|
|
|
|
|
for (int step = textureSize / 2; step >= 1; step /= 2) { |
|
|
|
|
|
|
|
#pragma omp parallel for collapse(2) |
|
|
|
|
|
|
|
for (int y = 0; y < textureSize; ++y) { |
|
|
|
|
|
|
|
for (int x = 0; x < textureSize; ++x) { |
|
|
|
|
|
|
|
float minDist = distanceField.at<float>(y, x); |
|
|
|
|
|
|
|
int bestIdx = nearestFaceIdx.at<int>(y, x); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int dy = -1; dy <= 1; dy += 2) { |
|
|
|
|
|
|
|
for (int dx = -1; dx <= 1; dx += 2) { |
|
|
|
|
|
|
|
int nx = x + dx * step; |
|
|
|
|
|
|
|
int ny = y + dy * step; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (nx >= 0 && nx < textureSize && ny >= 0 && ny < textureSize) { |
|
|
|
|
|
|
|
float dist = distanceField.at<float>(ny, nx) + |
|
|
|
|
|
|
|
sqrtf((dx*dx + dy*dy) * step * step); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (dist < minDist) { |
|
|
|
|
|
|
|
minDist = dist; |
|
|
|
|
|
|
|
bestIdx = nearestFaceIdx.at<int>(ny, nx); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
distanceField.at<float>(y, x) = minDist; |
|
|
|
|
|
|
|
nearestFaceIdx.at<int>(y, x) = bestIdx; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}void FillTextureGaps(Image8U3& textureAtlas, const Mesh::TexCoordArr& faceTexcoords, |
|
|
|
|
|
|
|
FIndex nFaces, const MeshTexture::LabelArr& faceLabels, |
|
|
|
|
|
|
|
int textureSize, Pixel8U colEmpty) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
DEBUG_EXTRA("开始填充纹理间隙..."); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建距离场
|
|
|
|
|
|
|
|
cv::Mat distanceField(textureSize, textureSize, CV_32FC1, cv::Scalar(std::numeric_limits<float>::max())); |
|
|
|
|
|
|
|
cv::Mat nearestPixelIdx(textureSize, textureSize, CV_32SC1, cv::Scalar(-1)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 初始化种子点(已有纹理的像素)
|
|
|
|
|
|
|
|
for (int y = 0; y < textureSize; ++y) { |
|
|
|
|
|
|
|
for (int x = 0; x < textureSize; ++x) { |
|
|
|
|
|
|
|
const Pixel8U& pixel = textureAtlas(y, x); |
|
|
|
|
|
|
|
if (pixel[0] != colEmpty[0] || pixel[1] != colEmpty[1] || pixel[2] != colEmpty[2]) { |
|
|
|
|
|
|
|
distanceField.at<float>(y, x) = 0.0f; |
|
|
|
|
|
|
|
nearestPixelIdx.at<int>(y, x) = y * textureSize + x; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 跳点传播算法
|
|
|
|
|
|
|
|
for (int step = textureSize / 2; step >= 1; step /= 2) { |
|
|
|
|
|
|
|
for (int y = 0; y < textureSize; ++y) { |
|
|
|
|
|
|
|
for (int x = 0; x < textureSize; ++x) { |
|
|
|
|
|
|
|
float minDist = distanceField.at<float>(y, x); |
|
|
|
|
|
|
|
int bestIdx = nearestPixelIdx.at<int>(y, x); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int dy = -1; dy <= 1; dy += 2) { |
|
|
|
|
|
|
|
for (int dx = -1; dx <= 1; dx += 2) { |
|
|
|
|
|
|
|
int nx = x + dx * step; |
|
|
|
|
|
|
|
int ny = y + dy * step; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (nx >= 0 && nx < textureSize && ny >= 0 && ny < textureSize) { |
|
|
|
|
|
|
|
float neighborDist = distanceField.at<float>(ny, nx); |
|
|
|
|
|
|
|
float dist = neighborDist + sqrtf((dx*dx + dy*dy) * step * step); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (dist < minDist && nearestPixelIdx.at<int>(ny, nx) != -1) { |
|
|
|
|
|
|
|
minDist = dist; |
|
|
|
|
|
|
|
bestIdx = nearestPixelIdx.at<int>(ny, nx); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (bestIdx != -1 && minDist < distanceField.at<float>(y, x)) { |
|
|
|
|
|
|
|
distanceField.at<float>(y, x) = minDist; |
|
|
|
|
|
|
|
nearestPixelIdx.at<int>(y, x) = bestIdx; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 填充空白区域
|
|
|
|
|
|
|
|
const float maxFillDistance = 5.0f; |
|
|
|
|
|
|
|
int filledCount = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int y = 0; y < textureSize; ++y) { |
|
|
|
|
|
|
|
for (int x = 0; x < textureSize; ++x) { |
|
|
|
|
|
|
|
Pixel8U& pixel = textureAtlas(y, x); |
|
|
|
|
|
|
|
if (pixel[0] == colEmpty[0] && pixel[1] == colEmpty[1] && pixel[2] == colEmpty[2]) { |
|
|
|
|
|
|
|
int nearestIdx = nearestPixelIdx.at<int>(y, x); |
|
|
|
|
|
|
|
float dist = distanceField.at<float>(y, x); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (nearestIdx != -1 && dist <= maxFillDistance) { |
|
|
|
|
|
|
|
int nx = nearestIdx % textureSize; |
|
|
|
|
|
|
|
int ny = nearestIdx / textureSize; |
|
|
|
|
|
|
|
pixel = textureAtlas(ny, nx); |
|
|
|
|
|
|
|
filledCount++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("快速填充完成,填充了 %d 个像素", filledCount); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// // 4. 优化版图像修复
|
|
|
|
|
|
|
|
// cv::Mat mask = cv::Mat::zeros(textureSize, textureSize, CV_8UC1);
|
|
|
|
|
|
|
|
// cv::Mat textureMat = textureAtlas;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// // 使用OpenCV向量化操作
|
|
|
|
|
|
|
|
// unsigned char r = colEmpty[0];
|
|
|
|
|
|
|
|
// unsigned char g = colEmpty[1];
|
|
|
|
|
|
|
|
// unsigned char b = colEmpty[2];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// #pragma omp parallel for
|
|
|
|
|
|
|
|
// for (int y = 0; y < textureSize; ++y) {
|
|
|
|
|
|
|
|
// unsigned char* maskRow = mask.ptr<unsigned char>(y);
|
|
|
|
|
|
|
|
// const unsigned char* textureRow = textureMat.ptr<unsigned char>(y);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// for (int x = 0; x < textureSize; ++x) {
|
|
|
|
|
|
|
|
// int idx = x * 3;
|
|
|
|
|
|
|
|
// if (textureRow[idx] == r &&
|
|
|
|
|
|
|
|
// textureRow[idx + 1] == g &&
|
|
|
|
|
|
|
|
// textureRow[idx + 2] == b) {
|
|
|
|
|
|
|
|
// maskRow[x] = 255;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// if (cv::countNonZero(mask) > 0) {
|
|
|
|
|
|
|
|
// // 优化inpaint参数
|
|
|
|
|
|
|
|
// int inpaintRadius = 2; // 减少修复半径
|
|
|
|
|
|
|
|
// cv::Mat inpaintResult;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// // 如果空白区域很少,使用更快的算法
|
|
|
|
|
|
|
|
// double blankRatio = cv::countNonZero(mask) / (double)(textureSize * textureSize);
|
|
|
|
|
|
|
|
// if (blankRatio < 0.01) { // 空白区域少于1%
|
|
|
|
|
|
|
|
// // 使用快速修复
|
|
|
|
|
|
|
|
// cv::dilate(textureMat, inpaintResult, cv::Mat());
|
|
|
|
|
|
|
|
// inpaintResult.copyTo(textureAtlas, mask);
|
|
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
|
|
// // 使用inpaint
|
|
|
|
|
|
|
|
// cv::inpaint(textureMat, mask, inpaintResult, inpaintRadius, cv::INPAINT_TELEA);
|
|
|
|
|
|
|
|
// inpaintResult.copyTo(textureAtlas);
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("纹理间隙填充完成"); |
|
|
|
DEBUG_EXTRA("纹理间隙填充完成"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -10089,8 +10296,7 @@ Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasFromUV(const LabelArr& faceLa |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 5. 添加后处理填充函数
|
|
|
|
// 5. 添加后处理填充函数
|
|
|
|
|
|
|
|
FillTextureGaps(textureAtlas, scene.mesh.faceTexcoords, (FIndex)scene.mesh.faces.size(), faceLabels, textureSize, colEmpty); |
|
|
|
FillTextureGaps(textureAtlas, scene.mesh.faceTexcoords, (FIndex)scene.mesh.faces.size(), faceLabels, views, textureSize, colEmpty); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 应用后处理
|
|
|
|
// 6. 应用后处理
|
|
|
|
if (fSharpnessWeight > 0) { |
|
|
|
if (fSharpnessWeight > 0) { |
|
|
|
|