填满小黄点

This commit is contained in:
hesuicong committed 2026-10-21 11:13:44 +08:00
1 parent 272d3900d0
commit 74271d0ad6
1 file changed
+67 -52
+67 -52
View File
@@ -828,50 +828,6 @@ public:
Pixel8U colEmpty, Pixel8U colEmpty,
Mesh::Image8U3Arr& outTextures); Mesh::Image8U3Arr& outTextures);
inline float ComputeLocalVariance(const cv::Mat& image, int patchW, int patchH) {
cv::Mat gray;
if (image.channels() == 3) {
cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
} else {
gray = image.clone();
}
// 1. 局部方差
cv::Mat mean, sqrMean;
cv::boxFilter(gray, mean, CV_32F, cv::Size(5, 5));
cv::boxFilter(gray.mul(gray), sqrMean, CV_32F, cv::Size(5, 5));
cv::Mat variance = sqrMean - mean.mul(mean);
float varScore = cv::mean(variance)[0] / 0.08f;
// 2. 梯度幅值
cv::Mat gradX, gradY;
cv::Sobel(gray, gradX, CV_32F, 1, 0, 3);
cv::Sobel(gray, gradY, CV_32F, 0, 1, 3);
cv::Mat magnitude;
cv::magnitude(gradX, gradY, magnitude);
float gradScore = cv::mean(magnitude)[0] * 3.0f;
// 3. 直方图熵(可选,衡量纹理丰富度)
int histSize = 256;
float range[] = {0, 256};
const float* histRange = {range};
cv::Mat hist;
cv::calcHist(&gray, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange);
hist /= gray.total();
float entropy = 0;
for (int i = 0; i < histSize; i++) {
float p = hist.at<float>(i);
if (p > 0) entropy -= p * log2f(p);
}
float entropyScore = entropy / 8.0f; // 8 bits 的最大熵
// 综合评分(可调整权重)
float complexity = 0.4f * varScore + 0.4f * gradScore + 0.2f * entropyScore;
return std::min(complexity, 1.0f);
}
inline bool ComputeBarycentric(float u, float v, inline bool ComputeBarycentric(float u, float v,
const TexCoord& uv0, const TexCoord& uv1, const TexCoord& uv2, const TexCoord& uv0, const TexCoord& uv1, const TexCoord& uv2,
float& b0, float& b1, float& b2) { float& b0, float& b1, float& b2) {
@@ -16708,8 +16664,8 @@ bool MeshTexture::RasterizeVirtualFaces(
mainPatch.convertTo(mainFloat, CV_32FC3, 1.0f / 255.0f); mainPatch.convertTo(mainFloat, CV_32FC3, 1.0f / 255.0f);
cv::Mat mainLowFreq; cv::Mat mainLowFreq;
cv::GaussianBlur(mainFloat, mainLowFreq, cv::Size(5, 5), 1.5); cv::GaussianBlur(mainFloat, mainLowFreq, cv::Size(31, 31), 10.0);
cv::Mat highFreqAccum = (mainFloat - mainLowFreq) * 0.9f; cv::Mat highFreqAccum = (mainFloat - mainLowFreq) * 0.6f;
float highFreqTotalWeight = 0.6f; float highFreqTotalWeight = 0.6f;
if (hasLowFreqViews) { if (hasLowFreqViews) {
@@ -16828,10 +16784,8 @@ bool MeshTexture::RasterizeVirtualFaces(
} }
} }
// 使用自适应权重,根据纹理复杂度决定 // cv::Mat finalPatch = filledLowFreq * 0.7f + mainFloat * 0.3f;
float textureComplexity = ComputeLocalVariance(mainFloat, patchW, patchH); cv::Mat finalPatch = filledLowFreq;
float lowFreqWeight = 0.3f + 0.4f * (1.0f - textureComplexity);
cv::Mat finalPatch = filledLowFreq * lowFreqWeight + mainFloat * (1.0f - lowFreqWeight);
// 钳制到 [0, 1] // 钳制到 [0, 1]
cv::Mat clampedPatch; cv::Mat clampedPatch;
@@ -16863,8 +16817,7 @@ bool MeshTexture::RasterizeVirtualFaces(
if (minDist < pad) { if (minDist < pad) {
weight = minDist / (float)pad; weight = minDist / (float)pad;
// 使用线性衰减而非平方衰减 weight = weight * weight;
weight = std::min(minDist / (float)pad, 1.0f); // 原 weight * weight
} }
if (currentScore > m_texelScores[idx].score) { if (currentScore > m_texelScores[idx].score) {
@@ -17159,6 +17112,68 @@ bool MeshTexture::RasterizeVirtualFaces(
if (m_texelPatchID[i] != NO_ID) validPixels++; if (m_texelPatchID[i] != NO_ID) validPixels++;
DEBUG_EXTRA("m_texelPatchID: %d valid pixels out of %zu", validPixels, m_texelPatchID.size()); DEBUG_EXTRA("m_texelPatchID: %d valid pixels out of %zu", validPixels, m_texelPatchID.size());
// ★★★ 全局空洞终极填充(优化版)★★★
cv::Mat holeMask(textureSize, textureSize, CV_8UC1);
for (int y = 0; y < textureSize; ++y) {
for (int x = 0; x < textureSize; ++x) {
Pixel8U px = atlas(y, x);
holeMask.at<uchar>(y, x) = (px[0] == colEmpty.b && px[1] == colEmpty.g && px[2] == colEmpty.r) ? 255 : 0;
}
}
// 多次膨胀,逐步向内填充
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(5, 5));
cv::Mat dilatedMask;
cv::Mat prevMask = holeMask.clone();
for (int iter = 0; iter < 30; ++iter) { // 最多膨胀30次(约150像素半径)
cv::dilate(prevMask, dilatedMask, kernel);
// 找出新覆盖的区域
cv::Mat newPixels = dilatedMask & ~prevMask;
if (cv::countNonZero(newPixels) == 0) break; // 没有新像素了
// 对新像素赋值为最近的非空洞像素颜色
for (int y = 0; y < textureSize; ++y) {
for (int x = 0; x < textureSize; ++x) {
if (newPixels.at<uchar>(y, x)) {
// 在原始非空洞区域找最近的颜色
int bestDist = INT_MAX;
Pixel8U bestColor(128, 128, 128);
int rad = 3;
for (int dy = -rad; dy <= rad && bestDist > 1; ++dy) {
for (int dx = -rad; dx <= rad && bestDist > 1; ++dx) {
int sx = x + dx, sy = y + dy;
if (sx < 0 || sx >= textureSize || sy < 0 || sy >= textureSize) continue;
if (!holeMask.at<uchar>(sy, sx)) { // 原始非空洞像素
Pixel8U spx = atlas(sy, sx);
int dist = dx*dx + dy*dy;
if (dist < bestDist) {
bestDist = dist;
bestColor = spx;
}
}
}
}
atlas(y, x) = bestColor;
}
}
}
prevMask = dilatedMask;
}
// 如果还有剩余空洞,用整体平均色填充
cv::Scalar avgColor = cv::mean(atlas, holeMask == 0);
Pixel8U avgPx((uchar)avgColor[0], (uchar)avgColor[1], (uchar)avgColor[2]);
for (int y = 0; y < textureSize; ++y) {
for (int x = 0; x < textureSize; ++x) {
if (holeMask.at<uchar>(y, x)) {
atlas(y, x) = avgPx;
}
}
}
return true; return true;
} }