|
|
|
@ -772,6 +772,9 @@ public: |
|
|
|
const std::vector<IIndex>& texelPatchID, |
|
|
|
const std::vector<IIndex>& texelPatchID, |
|
|
|
int textureSize, |
|
|
|
int textureSize, |
|
|
|
int featherRadius=3); |
|
|
|
int featherRadius=3); |
|
|
|
|
|
|
|
void AlignPatchColors(Image8U3& atlas, |
|
|
|
|
|
|
|
const std::vector<IIndex>& texelPatchID, |
|
|
|
|
|
|
|
int textureSize); |
|
|
|
void GlobalPatchColorAlignment(Image8U3& atlas, int textureSize); |
|
|
|
void GlobalPatchColorAlignment(Image8U3& atlas, int textureSize); |
|
|
|
void LocalSeamBlending(Image8U3& atlas, int textureSize); |
|
|
|
void LocalSeamBlending(Image8U3& atlas, int textureSize); |
|
|
|
std::vector<Color> patchAvgColor; // ← 直接声明 vector,不要加括号!
|
|
|
|
std::vector<Color> patchAvgColor; // ← 直接声明 vector,不要加括号!
|
|
|
|
@ -14607,6 +14610,83 @@ void MeshTexture::GlobalPatchColorAlignment(Image8U3& atlas, int textureSize) |
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Global alignment done: %d constraints (%s)", rows, TD_TIMER_GET_FMT().c_str()); |
|
|
|
DEBUG_EXTRA("Global alignment done: %d constraints (%s)", rows, TD_TIMER_GET_FMT().c_str()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ========== Patch Color Alignment(改进版,不依赖 GlobalPatchColorAlignment)==========
|
|
|
|
|
|
|
|
void MeshTexture::AlignPatchColors(Image8U3& atlas, |
|
|
|
|
|
|
|
const std::vector<IIndex>& texelPatchID, |
|
|
|
|
|
|
|
int textureSize) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (atlas.empty() || texelPatchID.empty()) return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ---------- 一次遍历:记录每个 patch 的"向外边界颜色" ----------
|
|
|
|
|
|
|
|
// 边界像素的定义:它自己和某个邻居属于不同 patch。
|
|
|
|
|
|
|
|
// 对于这条边,颜色贡献到"我的 patch",同时也贡献到"邻居的 patch"。
|
|
|
|
|
|
|
|
struct Acc { cv::Vec3d sum = {0,0,0}; int count = 0; }; |
|
|
|
|
|
|
|
std::map<IIndex, Acc> myBorder; // patch -> 自己这侧的边界颜色累加
|
|
|
|
|
|
|
|
std::map<IIndex, Acc> neighborBorder; // patch -> 所有邻居边界颜色的累加
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const int dx4[] = { -1, 1, 0, 0 }; |
|
|
|
|
|
|
|
const int dy4[] = { 0, 0, -1, 1 }; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int y = 1; y < textureSize - 1; ++y) { |
|
|
|
|
|
|
|
for (int x = 1; x < textureSize - 1; ++x) { |
|
|
|
|
|
|
|
int idx = y * textureSize + x; |
|
|
|
|
|
|
|
if (texelPatchID[idx] == NO_ID) continue; |
|
|
|
|
|
|
|
IIndex myPatch = texelPatchID[idx]; |
|
|
|
|
|
|
|
const Pixel8U& myPx = atlas(y, x); |
|
|
|
|
|
|
|
cv::Vec3f myColor(myPx[2], myPx[1], myPx[0]); // BGR -> RGB
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int d = 0; d < 4; ++d) { |
|
|
|
|
|
|
|
int nx = x + dx4[d], ny = y + dy4[d]; |
|
|
|
|
|
|
|
int nidx = ny * textureSize + nx; |
|
|
|
|
|
|
|
IIndex nbPatch = texelPatchID[nidx]; |
|
|
|
|
|
|
|
if (nbPatch == NO_ID || nbPatch == myPatch) continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 这条边存在:两侧都记录
|
|
|
|
|
|
|
|
myBorder[myPatch].sum += cv::Vec3d(myColor); |
|
|
|
|
|
|
|
myBorder[myPatch].count++; |
|
|
|
|
|
|
|
neighborBorder[myPatch].sum += cv::Vec3d(atlas(ny, nx)[2], |
|
|
|
|
|
|
|
atlas(ny, nx)[1], |
|
|
|
|
|
|
|
atlas(ny, nx)[0]); |
|
|
|
|
|
|
|
neighborBorder[myPatch].count++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ---------- 计算增益(O(patch 数),不再扫全图)----------
|
|
|
|
|
|
|
|
std::map<IIndex, cv::Vec3f> patchGain; |
|
|
|
|
|
|
|
for (const auto& entry : myBorder) { |
|
|
|
|
|
|
|
IIndex pid = entry.first; |
|
|
|
|
|
|
|
if (entry.second.count == 0 || neighborBorder[pid].count == 0) continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cv::Vec3d myMean = entry.second.sum / entry.second.count; |
|
|
|
|
|
|
|
cv::Vec3d nbMean = neighborBorder[pid].sum / neighborBorder[pid].count; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cv::Vec3f gain(1, 1, 1); |
|
|
|
|
|
|
|
for (int c = 0; c < 3; ++c) { |
|
|
|
|
|
|
|
if (myMean[c] > 1.0f) |
|
|
|
|
|
|
|
gain[c] = (float)(nbMean[c] / myMean[c]); |
|
|
|
|
|
|
|
gain[c] = std::max(0.5f, std::min(2.0f, gain[c])); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
patchGain[pid] = gain; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ---------- 应用增益 ----------
|
|
|
|
|
|
|
|
for (int y = 0; y < textureSize; ++y) { |
|
|
|
|
|
|
|
for (int x = 0; x < textureSize; ++x) { |
|
|
|
|
|
|
|
int idx = y * textureSize + x; |
|
|
|
|
|
|
|
if (texelPatchID[idx] == NO_ID) continue; |
|
|
|
|
|
|
|
auto it = patchGain.find(texelPatchID[idx]); |
|
|
|
|
|
|
|
if (it == patchGain.end()) continue; |
|
|
|
|
|
|
|
const cv::Vec3f& gain = it->second; |
|
|
|
|
|
|
|
Pixel8U& px = atlas(y, x); |
|
|
|
|
|
|
|
px[2] = cv::saturate_cast<uchar>(px[2] * gain[0]); |
|
|
|
|
|
|
|
px[1] = cv::saturate_cast<uchar>(px[1] * gain[1]); |
|
|
|
|
|
|
|
px[0] = cv::saturate_cast<uchar>(px[0] * gain[2]); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 对每个 texel,如果它的 patchID 和邻居不同,说明是接缝
|
|
|
|
// 对每个 texel,如果它的 patchID 和邻居不同,说明是接缝
|
|
|
|
// 在接缝处做 3x3 或 5x5 的高斯模糊/均值模糊
|
|
|
|
// 在接缝处做 3x3 或 5x5 的高斯模糊/均值模糊
|
|
|
|
void MeshTexture::FeatherTextureSeams(Image8U3& texture, |
|
|
|
void MeshTexture::FeatherTextureSeams(Image8U3& texture, |
|
|
|
@ -14855,6 +14935,7 @@ bool MeshTexture::RasterizeVirtualFaces( |
|
|
|
DEBUG_EXTRA("Seam blending completed: %zu edges (%s)", seamEdges.size(), TD_TIMER_GET_FMT().c_str()); |
|
|
|
DEBUG_EXTRA("Seam blending completed: %zu edges (%s)", seamEdges.size(), TD_TIMER_GET_FMT().c_str()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AlignPatchColors(atlas, m_texelPatchID, textureSize); |
|
|
|
// FeatherTextureSeams(atlas, m_texelPatchID, textureSize, 3);
|
|
|
|
// FeatherTextureSeams(atlas, m_texelPatchID, textureSize, 3);
|
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|