填满小黄点
This commit is contained in:
1 parent
272d3900d0
commit
74271d0ad6
1 file changed
+172
-157
+172
-157
@@ -827,50 +827,6 @@ public:
|
||||
unsigned nTextureSizeMultiple,
|
||||
Pixel8U colEmpty,
|
||||
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,
|
||||
const TexCoord& uv0, const TexCoord& uv1, const TexCoord& uv2,
|
||||
@@ -16708,8 +16664,8 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
mainPatch.convertTo(mainFloat, CV_32FC3, 1.0f / 255.0f);
|
||||
|
||||
cv::Mat mainLowFreq;
|
||||
cv::GaussianBlur(mainFloat, mainLowFreq, cv::Size(5, 5), 1.5);
|
||||
cv::Mat highFreqAccum = (mainFloat - mainLowFreq) * 0.9f;
|
||||
cv::GaussianBlur(mainFloat, mainLowFreq, cv::Size(31, 31), 10.0);
|
||||
cv::Mat highFreqAccum = (mainFloat - mainLowFreq) * 0.6f;
|
||||
float highFreqTotalWeight = 0.6f;
|
||||
|
||||
if (hasLowFreqViews) {
|
||||
@@ -16739,83 +16695,83 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
|
||||
cv::Mat highFreq = highFreqAccum;
|
||||
|
||||
// ========== 每个顶点独立判断可见性(增强版)==========
|
||||
bool vertexVisible[3] = {false, false, false};
|
||||
cv::Vec3f vertexBestColor[3] = {cv::Vec3f(0,0,0), cv::Vec3f(0,0,0), cv::Vec3f(0,0,0)};
|
||||
// ========== 每个顶点独立判断可见性(增强版)==========
|
||||
bool vertexVisible[3] = {false, false, false};
|
||||
cv::Vec3f vertexBestColor[3] = {cv::Vec3f(0,0,0), cv::Vec3f(0,0,0), cv::Vec3f(0,0,0)};
|
||||
|
||||
// 检查主视图
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
int vx = (int)(uv[j].x * textureSize) - minX;
|
||||
int vy = (int)(uv[j].y * textureSize) - minY;
|
||||
if (vx >= 0 && vx < patchW && vy >= 0 && vy < patchH) {
|
||||
cv::Vec3f color = mainFloat.at<cv::Vec3f>(vy, vx);
|
||||
if (color[0] > 0.0f || color[1] > 0.0f || color[2] > 0.0f) {
|
||||
vertexVisible[j] = true;
|
||||
vertexBestColor[j] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 检查主视图
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
int vx = (int)(uv[j].x * textureSize) - minX;
|
||||
int vy = (int)(uv[j].y * textureSize) - minY;
|
||||
if (vx >= 0 && vx < patchW && vy >= 0 && vy < patchH) {
|
||||
cv::Vec3f color = mainFloat.at<cv::Vec3f>(vy, vx);
|
||||
if (color[0] > 0.0f || color[1] > 0.0f || color[2] > 0.0f) {
|
||||
vertexVisible[j] = true;
|
||||
vertexBestColor[j] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果主视图不可见,检查辅助视图
|
||||
if (hasLowFreqViews && (!vertexVisible[0] || !vertexVisible[1] || !vertexVisible[2])) {
|
||||
for (const auto& lfv : lowFreqViews) {
|
||||
if (lfv.viewId >= (IIndex)images.size() || images[lfv.viewId].image.empty()) continue;
|
||||
|
||||
// 对该辅助视图重新采样该顶点位置
|
||||
const Camera& auxCam = images[lfv.viewId].camera;
|
||||
bool auxCamValid = (auxCam.K(0,0) > 0 && auxCam.K(1,1) > 0);
|
||||
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
if (vertexVisible[j]) continue; // 已经可见就跳过
|
||||
|
||||
// 用3D投影得到该顶点在辅助视图中的像素位置
|
||||
Point2f imgPt;
|
||||
if (auxCamValid) {
|
||||
imgPt = auxCam.ProjectPointP(v[j]);
|
||||
} else {
|
||||
// 用 homography fallback
|
||||
const auto& geomRef = m_virtualFaceGeometries[i];
|
||||
auto it = geomRef.viewHomographies.find(lfv.viewId);
|
||||
if (it == geomRef.viewHomographies.end()) continue;
|
||||
|
||||
const float* H = it->second.ptr<float>();
|
||||
float u = uv[j].x;
|
||||
float v_coord = uv[j].y;
|
||||
float w = H[6]*u + H[7]*v_coord + H[8];
|
||||
if (std::abs(w) < 1e-12f) continue;
|
||||
|
||||
float projX = (H[0]*u + H[1]*v_coord + H[2]) / w;
|
||||
float projY = (H[3]*u + H[4]*v_coord + H[5]) / w;
|
||||
imgPt = Point2f(projX, projY);
|
||||
}
|
||||
|
||||
if (imgPt.x >= 0 && imgPt.x < images[lfv.viewId].image.cols &&
|
||||
imgPt.y >= 0 && imgPt.y < images[lfv.viewId].image.rows) {
|
||||
// 采样该像素颜色
|
||||
cv::Vec3f auxColor = imagesFloat[lfv.viewId].at<cv::Vec3f>((int)imgPt.y, (int)imgPt.x);
|
||||
if (auxColor[0] > 0.0f || auxColor[1] > 0.0f || auxColor[2] > 0.0f) {
|
||||
vertexVisible[j] = true;
|
||||
vertexBestColor[j] = auxColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果三个顶点都已可见,提前退出
|
||||
if (vertexVisible[0] && vertexVisible[1] && vertexVisible[2])
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 如果主视图不可见,检查辅助视图
|
||||
if (hasLowFreqViews && (!vertexVisible[0] || !vertexVisible[1] || !vertexVisible[2])) {
|
||||
for (const auto& lfv : lowFreqViews) {
|
||||
if (lfv.viewId >= (IIndex)images.size() || images[lfv.viewId].image.empty()) continue;
|
||||
|
||||
// 对该辅助视图重新采样该顶点位置
|
||||
const Camera& auxCam = images[lfv.viewId].camera;
|
||||
bool auxCamValid = (auxCam.K(0,0) > 0 && auxCam.K(1,1) > 0);
|
||||
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
if (vertexVisible[j]) continue; // 已经可见就跳过
|
||||
|
||||
// 用3D投影得到该顶点在辅助视图中的像素位置
|
||||
Point2f imgPt;
|
||||
if (auxCamValid) {
|
||||
imgPt = auxCam.ProjectPointP(v[j]);
|
||||
} else {
|
||||
// 用 homography fallback
|
||||
const auto& geomRef = m_virtualFaceGeometries[i];
|
||||
auto it = geomRef.viewHomographies.find(lfv.viewId);
|
||||
if (it == geomRef.viewHomographies.end()) continue;
|
||||
|
||||
const float* H = it->second.ptr<float>();
|
||||
float u = uv[j].x;
|
||||
float v_coord = uv[j].y;
|
||||
float w = H[6]*u + H[7]*v_coord + H[8];
|
||||
if (std::abs(w) < 1e-12f) continue;
|
||||
|
||||
float projX = (H[0]*u + H[1]*v_coord + H[2]) / w;
|
||||
float projY = (H[3]*u + H[4]*v_coord + H[5]) / w;
|
||||
imgPt = Point2f(projX, projY);
|
||||
}
|
||||
|
||||
if (imgPt.x >= 0 && imgPt.x < images[lfv.viewId].image.cols &&
|
||||
imgPt.y >= 0 && imgPt.y < images[lfv.viewId].image.rows) {
|
||||
// 采样该像素颜色
|
||||
cv::Vec3f auxColor = imagesFloat[lfv.viewId].at<cv::Vec3f>((int)imgPt.y, (int)imgPt.x);
|
||||
if (auxColor[0] > 0.0f || auxColor[1] > 0.0f || auxColor[2] > 0.0f) {
|
||||
vertexVisible[j] = true;
|
||||
vertexBestColor[j] = auxColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果三个顶点都已可见,提前退出
|
||||
if (vertexVisible[0] && vertexVisible[1] && vertexVisible[2])
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma omp critical (vertex_visibility)
|
||||
{
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
VIndex vi = face[j];
|
||||
if (vi < m_vertexColors.size() && vertexVisible[j]) {
|
||||
m_vertexColors[vi].visibleInAnyView = true;
|
||||
m_vertexColors[vi].mainColor = vertexBestColor[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma omp critical (vertex_visibility)
|
||||
{
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
VIndex vi = face[j];
|
||||
if (vi < m_vertexColors.size() && vertexVisible[j]) {
|
||||
m_vertexColors[vi].visibleInAnyView = true;
|
||||
m_vertexColors[vi].mainColor = vertexBestColor[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 空洞填充与合成 ==========
|
||||
cv::Mat filledLowFreq = lowFreqPatch.clone();
|
||||
@@ -16828,10 +16784,8 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
}
|
||||
}
|
||||
|
||||
// 使用自适应权重,根据纹理复杂度决定
|
||||
float textureComplexity = ComputeLocalVariance(mainFloat, patchW, patchH);
|
||||
float lowFreqWeight = 0.3f + 0.4f * (1.0f - textureComplexity);
|
||||
cv::Mat finalPatch = filledLowFreq * lowFreqWeight + mainFloat * (1.0f - lowFreqWeight);
|
||||
// cv::Mat finalPatch = filledLowFreq * 0.7f + mainFloat * 0.3f;
|
||||
cv::Mat finalPatch = filledLowFreq;
|
||||
|
||||
// 钳制到 [0, 1]
|
||||
cv::Mat clampedPatch;
|
||||
@@ -16863,8 +16817,7 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
|
||||
if (minDist < pad) {
|
||||
weight = minDist / (float)pad;
|
||||
// 使用线性衰减而非平方衰减
|
||||
weight = std::min(minDist / (float)pad, 1.0f); // 原 weight * weight
|
||||
weight = weight * weight;
|
||||
}
|
||||
|
||||
if (currentScore > m_texelScores[idx].score) {
|
||||
@@ -16967,35 +16920,35 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
continue;
|
||||
|
||||
// 找到这个像素属于哪个面
|
||||
int patchID = m_texelPatchID[idx];
|
||||
if (patchID >= (int)virtualFaceMap.size())
|
||||
continue;
|
||||
int patchID = m_texelPatchID[idx];
|
||||
if (patchID >= (int)virtualFaceMap.size())
|
||||
continue;
|
||||
|
||||
if (patchID == NO_ID) {
|
||||
// 尝试找最近的已填充像素
|
||||
int bestDist = INT_MAX;
|
||||
int bestPatchID = NO_ID;
|
||||
int searchRad = 16;
|
||||
for (int dy = -searchRad; dy <= searchRad; ++dy) {
|
||||
for (int dx = -searchRad; dx <= searchRad; ++dx) {
|
||||
int sx = atlasX + dx, sy = atlasY + dy;
|
||||
if (sx < 0 || sx >= textureSize || sy < 0 || sy >= textureSize) continue;
|
||||
size_t sidx = sy * textureSize + sx;
|
||||
if (m_texelPatchID[sidx] != NO_ID) {
|
||||
int dist = dx*dx + dy*dy;
|
||||
if (dist < bestDist) {
|
||||
bestDist = dist;
|
||||
bestPatchID = m_texelPatchID[sidx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestPatchID != NO_ID) {
|
||||
patchID = bestPatchID;
|
||||
} else {
|
||||
continue; // 实在找不到归属面,跳过
|
||||
}
|
||||
}
|
||||
if (patchID == NO_ID) {
|
||||
// 尝试找最近的已填充像素
|
||||
int bestDist = INT_MAX;
|
||||
int bestPatchID = NO_ID;
|
||||
int searchRad = 16;
|
||||
for (int dy = -searchRad; dy <= searchRad; ++dy) {
|
||||
for (int dx = -searchRad; dx <= searchRad; ++dx) {
|
||||
int sx = atlasX + dx, sy = atlasY + dy;
|
||||
if (sx < 0 || sx >= textureSize || sy < 0 || sy >= textureSize) continue;
|
||||
size_t sidx = sy * textureSize + sx;
|
||||
if (m_texelPatchID[sidx] != NO_ID) {
|
||||
int dist = dx*dx + dy*dy;
|
||||
if (dist < bestDist) {
|
||||
bestDist = dist;
|
||||
bestPatchID = m_texelPatchID[sidx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestPatchID != NO_ID) {
|
||||
patchID = bestPatchID;
|
||||
} else {
|
||||
continue; // 实在找不到归属面,跳过
|
||||
}
|
||||
}
|
||||
|
||||
const VirtualFace& vf = virtualFaceMap[patchID];
|
||||
if (vf.faces.empty()) continue;
|
||||
@@ -17041,7 +16994,7 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
fillColor = vertexColors[bestVertex];
|
||||
}
|
||||
|
||||
// ★★★ 新增:检查 fillColor 是否有效(接近黑色)★★★
|
||||
// ★★★ 新增:检查 fillColor 是否有效(接近黑色)★★★
|
||||
bool fillIsBlack = (fillColor[0] < 0.05f && fillColor[1] < 0.05f && fillColor[2] < 0.05f);
|
||||
if (fillIsBlack) {
|
||||
// Fallback:从邻近非空洞像素采样
|
||||
@@ -17157,11 +17110,73 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
int validPixels = 0;
|
||||
for (size_t i = 0; i < m_texelPatchID.size(); ++i)
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
bool MeshTexture::BuildSeamVertsFromEdges()
|
||||
{
|
||||
const size_t NP = rcPatches.size();
|
||||
|
||||
Reference in new issue
Block a user