解决运行问题
This commit is contained in:
1 parent
881bf55089
commit
6e0e0f9cc7
1 file changed
+138
-151
+138
-151
@@ -669,6 +669,7 @@ public:
|
||||
const Mesh::TexCoordArr& existingTexcoords, // 添加已有UV参数
|
||||
const Mesh::TexIndexArr& existingTexindices // 添加已有纹理索引参数
|
||||
);
|
||||
|
||||
bool RasterizeVirtualFaces(
|
||||
const VirtualFaceMap& virtualFaceMap,
|
||||
const std::vector<std::vector<IIndex>>& virtualFaceViews,
|
||||
@@ -727,31 +728,56 @@ public:
|
||||
// ========================================================================
|
||||
// 辅助函数:双线性采样
|
||||
// ========================================================================
|
||||
inline Color BilinearSample(const Image8U3& img, const Point2f& p) {
|
||||
const int w = img.width();
|
||||
const int h = img.height();
|
||||
const float fx = p.x - 0.5f; // 像素中心对齐
|
||||
inline Color BilinearSample(const Image8U3& img, const Point2f& p)
|
||||
{
|
||||
// ✅ 防御 NaN / Inf
|
||||
if (!std::isfinite(p.x) || !std::isfinite(p.y))
|
||||
return Color(0, 0, 0);
|
||||
|
||||
// ✅ 防御空图
|
||||
if (img.empty() || img.cols < 2 || img.rows < 2)
|
||||
return Color(0, 0, 0);
|
||||
|
||||
const float fx = p.x - 0.5f;
|
||||
const float fy = p.y - 0.5f;
|
||||
int x0 = (int)std::floor(fx);
|
||||
int y0 = (int)std::floor(fy);
|
||||
float dx = fx - x0;
|
||||
float dy = fy - y0;
|
||||
|
||||
auto get = [&](int x, int y) -> Color {
|
||||
x = std::max(0, std::min(x, w - 1));
|
||||
y = std::max(0, std::min(y, h - 1));
|
||||
return img(x, y);
|
||||
};
|
||||
const int x0 = static_cast<int>(std::floor(fx));
|
||||
const int y0 = static_cast<int>(std::floor(fy));
|
||||
const float dx = fx - x0;
|
||||
const float dy = fy - y0;
|
||||
|
||||
Color c00 = get(x0, y0);
|
||||
Color c10 = get(x0 + 1, y0);
|
||||
Color c01 = get(x0, y0 + 1);
|
||||
Color c11 = get(x0 + 1, y0 + 1);
|
||||
// ✅ 安全 clamp(OpenMVS 最稳写法)
|
||||
const int x1 = std::min(x0 + 1, img.cols - 1);
|
||||
const int y1 = std::min(y0 + 1, img.rows - 1);
|
||||
const int x0c = std::max(x0, 0);
|
||||
const int y0c = std::max(y0, 0);
|
||||
|
||||
return c00 * ((1.0f - dx) * (1.0f - dy))
|
||||
+ c10 * (dx * (1.0f - dy))
|
||||
+ c01 * ((1.0f - dx) * dy)
|
||||
+ c11 * (dx * dy);
|
||||
// ✅ 用 ptr 直接访问(最快、最稳)
|
||||
const cv::Vec3b* row0 = img.ptr<const cv::Vec3b>(y0c);
|
||||
const cv::Vec3b* row1 = img.ptr<const cv::Vec3b>(y1);
|
||||
|
||||
const cv::Vec3b& c00 = row0[x0c];
|
||||
const cv::Vec3b& c10 = row0[x1];
|
||||
const cv::Vec3b& c01 = row1[x0c];
|
||||
const cv::Vec3b& c11 = row1[x1];
|
||||
|
||||
// BGR → RGB
|
||||
return Color(
|
||||
c00[2] * ((1.0f - dx) * (1.0f - dy)) +
|
||||
c10[2] * (dx * (1.0f - dy)) +
|
||||
c01[2] * ((1.0f - dx) * dy) +
|
||||
c11[2] * (dx * dy),
|
||||
|
||||
c00[1] * ((1.0f - dx) * (1.0f - dy)) +
|
||||
c10[1] * (dx * (1.0f - dy)) +
|
||||
c01[1] * ((1.0f - dx) * dy) +
|
||||
c11[1] * (dx * dy),
|
||||
|
||||
c00[0] * ((1.0f - dx) * (1.0f - dy)) +
|
||||
c10[0] * (dx * (1.0f - dy)) +
|
||||
c01[0] * ((1.0f - dx) * dy) +
|
||||
c11[0] * (dx * dy)
|
||||
);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
@@ -14146,14 +14172,6 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
DEBUG_EXTRA("Forward Rasterization Engine: Starting...");
|
||||
TD_TIMER_START();
|
||||
|
||||
int totalVF = virtualFaceMap.size();
|
||||
int emptyVF = 0;
|
||||
for (auto& vf : virtualFaceViews)
|
||||
if (vf.empty()) ++emptyVF;
|
||||
|
||||
DEBUG_EXTRA("VirtualFaces: total=%d empty=%d (%.1f%%)",
|
||||
totalVF, emptyVF, 100.0f*emptyVF/totalVF);
|
||||
|
||||
if (virtualFaceMap.empty() || virtualFaceViews.size() != virtualFaceMap.size())
|
||||
return false;
|
||||
|
||||
@@ -14164,9 +14182,9 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
for (const TexCoord& uv : scene.mesh.faceTexcoords)
|
||||
uvBounds.InsertFull(uv);
|
||||
|
||||
float uvWidth = uvBounds.ptMax.x() - uvBounds.ptMin.x();
|
||||
float uvWidth = uvBounds.ptMax.x() - uvBounds.ptMin.x();
|
||||
float uvHeight = uvBounds.ptMax.y() - uvBounds.ptMin.y();
|
||||
if (uvWidth < 0.001f) uvWidth = 1.0f;
|
||||
if (uvWidth < 0.001f) uvWidth = 1.0f;
|
||||
if (uvHeight < 0.001f) uvHeight = 1.0f;
|
||||
|
||||
int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple);
|
||||
@@ -14178,66 +14196,80 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
Image8U3& atlas = outTextures.back();
|
||||
atlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r));
|
||||
|
||||
// ✅ 深度缓冲器和面片ID缓冲器
|
||||
cv::Mat1f depthBuffer(textureSize, textureSize, -FLT_MAX);
|
||||
cv::Mat1i faceIDBuffer(textureSize, textureSize, -1);
|
||||
|
||||
// ✅ 颜色缓冲器
|
||||
// ✅ 深度缓冲器初始化为正无穷(越小越近)
|
||||
cv::Mat1f depthBuffer(textureSize, textureSize, FLT_MAX);
|
||||
cv::Mat3f colorBuffer(textureSize, textureSize, cv::Vec3f(0.f, 0.f, 0.f));
|
||||
cv::Mat1b validBuffer(textureSize, textureSize, (uchar)0);
|
||||
|
||||
// --------------------------------------------------
|
||||
// 3. 光栅化参数 - 使用单采样保证清晰度
|
||||
// --------------------------------------------------
|
||||
constexpr int SUPER_SAMPLE = 1; // ✅ 关键:禁用MSAA,使用单采样
|
||||
constexpr float STEP = 1.f / SUPER_SAMPLE;
|
||||
|
||||
// --------------------------------------------------
|
||||
// 4. 正向光栅化主循环
|
||||
// 3. 正向光栅化主循环
|
||||
// --------------------------------------------------
|
||||
#ifdef _USE_OPENMP
|
||||
#pragma omp parallel for schedule(dynamic)
|
||||
#endif
|
||||
for (int_t idxVF = 0; idxVF < (int_t)virtualFaceMap.size(); ++idxVF)
|
||||
{
|
||||
for (int_t idxVF = 0; idxVF < (int_t)virtualFaceMap.size(); ++idxVF) {
|
||||
if (virtualFaceViews[idxVF].empty())
|
||||
continue;
|
||||
|
||||
const IIndex viewID = virtualFaceViews[idxVF][0]; // 单视图
|
||||
const IIndex viewID = virtualFaceViews[idxVF][0];
|
||||
if (viewID >= (IIndex)images.size()) continue;
|
||||
|
||||
const Image& srcImg = images[viewID];
|
||||
if (srcImg.image.empty() || srcImg.image.cols < 2 || srcImg.image.rows < 2)
|
||||
continue;
|
||||
|
||||
const Camera& cam = srcImg.camera;
|
||||
const int srcW = srcImg.image.cols;
|
||||
const int srcH = srcImg.image.rows;
|
||||
|
||||
for (FIndex faceID : virtualFaceMap[idxVF].faces)
|
||||
{
|
||||
for (FIndex faceID : virtualFaceMap[idxVF].faces) {
|
||||
if (faceID >= (FIndex)scene.mesh.faces.size()) continue;
|
||||
|
||||
const Face& face = scene.mesh.faces[faceID];
|
||||
const TexCoord* uv = &scene.mesh.faceTexcoords[faceID * 3];
|
||||
const Vertex* verts[3] = {
|
||||
|
||||
// 验证顶点索引
|
||||
bool validIndices = true;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (face[i] >= scene.mesh.vertices.size()) {
|
||||
validIndices = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!validIndices) continue;
|
||||
|
||||
const Point3f* verts[3] = {
|
||||
&scene.mesh.vertices[face[0]],
|
||||
&scene.mesh.vertices[face[1]],
|
||||
&scene.mesh.vertices[face[2]]
|
||||
};
|
||||
|
||||
// ✅ 关键修复1:扩大包围盒,确保覆盖所有像素
|
||||
// 计算包围盒
|
||||
float minU = std::min({uv[0].x, uv[1].x, uv[2].x});
|
||||
float maxU = std::max({uv[0].x, uv[1].x, uv[2].x});
|
||||
float minV = std::min({uv[0].y, uv[1].y, uv[2].y});
|
||||
float maxV = std::max({uv[0].y, uv[1].y, uv[2].y});
|
||||
|
||||
int minX = std::max(0, (int)floor(minU * textureSize) - 1);
|
||||
int maxX = std::min(textureSize - 1, (int)ceil(maxU * textureSize) + 1);
|
||||
int minY = std::max(0, (int)floor(minV * textureSize) - 1);
|
||||
int maxY = std::min(textureSize - 1, (int)ceil(maxV * textureSize) + 1);
|
||||
int minX = std::max(0, (int)floor(minU * textureSize));
|
||||
int maxX = std::min(textureSize - 1, (int)ceil(maxU * textureSize));
|
||||
int minY = std::max(0, (int)floor(minV * textureSize));
|
||||
int maxY = std::min(textureSize - 1, (int)ceil(maxV * textureSize));
|
||||
|
||||
if (minX > maxX || minY > maxY) continue;
|
||||
|
||||
// ✅ 关键修复2:预计算三角形深度范围
|
||||
Point3f triCenter = (*verts[0] + *verts[1] + *verts[2]) / 3.0f;
|
||||
float triDepth = cam.PointDepth(triCenter);
|
||||
float depthRange = 0.1f * triDepth; // 10%的深度容差
|
||||
// ✅ 预计算顶点深度(用于透视校正)
|
||||
float vertexDepths[3];
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
vertexDepths[i] = cam.PointDepth(*verts[i]);
|
||||
if (vertexDepths[i] <= 0.0f) {
|
||||
validIndices = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!validIndices) continue;
|
||||
|
||||
for (int y = minY; y <= maxY; ++y) {
|
||||
for (int x = minX; x <= maxX; ++x) {
|
||||
|
||||
Point2f texCoord(
|
||||
(x + 0.5f) / textureSize,
|
||||
(y + 0.5f) / textureSize
|
||||
@@ -14247,119 +14279,74 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
if (!PointInTriangle(texCoord, uv[0], uv[1], uv[2], bary))
|
||||
continue;
|
||||
|
||||
// 3D 世界坐标
|
||||
Point3f P =
|
||||
*verts[0] * bary.x +
|
||||
*verts[1] * bary.y +
|
||||
*verts[2] * bary.z;
|
||||
|
||||
// ✅ 关键修复3:简化的深度测试
|
||||
float depth = cam.PointDepth(P);
|
||||
// ✅ 透视校正插值
|
||||
// 计算插值后的1/z
|
||||
float invZ = bary.x / vertexDepths[0] +
|
||||
bary.y / vertexDepths[1] +
|
||||
bary.z / vertexDepths[2];
|
||||
if (invZ <= 0.0f) continue;
|
||||
|
||||
// 放宽深度测试:允许一定范围的深度差异
|
||||
if (depth < triDepth - depthRange || depth > triDepth + depthRange)
|
||||
continue;
|
||||
float depth = 1.0f / invZ; // 校正后的深度
|
||||
|
||||
// ✅ 关键修复4:严格的面片ID检查
|
||||
bool accept = false;
|
||||
#pragma omp critical
|
||||
{
|
||||
// 如果像素未被占用,或者当前面片ID相同,或者深度更近
|
||||
if (faceIDBuffer(y, x) == -1 ||
|
||||
faceIDBuffer(y, x) == (int)faceID ||
|
||||
depth > depthBuffer(y, x)) {
|
||||
|
||||
// 更新深度和面片ID
|
||||
depthBuffer(y, x) = depth;
|
||||
faceIDBuffer(y, x) = (int)faceID;
|
||||
accept = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!accept) continue;
|
||||
// ✅ 透视校正的世界坐标
|
||||
Point3f P(
|
||||
(verts[0]->x * bary.x / vertexDepths[0] +
|
||||
verts[1]->x * bary.y / vertexDepths[1] +
|
||||
verts[2]->x * bary.z / vertexDepths[2]) * depth,
|
||||
(verts[0]->y * bary.x / vertexDepths[0] +
|
||||
verts[1]->y * bary.y / vertexDepths[1] +
|
||||
verts[2]->y * bary.z / vertexDepths[2]) * depth,
|
||||
(verts[0]->z * bary.x / vertexDepths[0] +
|
||||
verts[1]->z * bary.y / vertexDepths[1] +
|
||||
verts[2]->z * bary.z / vertexDepths[2]) * depth
|
||||
);
|
||||
|
||||
// 投影到图像
|
||||
Point2f imgPt = ProjectPointWithAutoCorrection(cam, P, srcImg);
|
||||
|
||||
// ✅ 关键修复5:改进的边界处理
|
||||
if (!srcImg.image.isInside(imgPt)) {
|
||||
// 尝试在图像边界内寻找最近的有效点
|
||||
imgPt.x = std::clamp(imgPt.x, 0.f, srcImg.image.width() - 1.1f);
|
||||
imgPt.y = std::clamp(imgPt.y, 0.f, srcImg.image.height() - 1.1f);
|
||||
}
|
||||
// ✅ 正确的深度测试:只保留更近的像素
|
||||
if (depth <= 0.0f) continue;
|
||||
|
||||
// 前后判断
|
||||
if (!cam.IsInFront(P) && cam.Distance(P) < -0.01f)
|
||||
continue;
|
||||
Point3d P_double(
|
||||
(verts[0]->x * bary.x / vertexDepths[0] +
|
||||
verts[1]->x * bary.y / vertexDepths[1] +
|
||||
verts[2]->x * bary.z / vertexDepths[2]) / invZ,
|
||||
(verts[0]->y * bary.x / vertexDepths[0] +
|
||||
verts[1]->y * bary.y / vertexDepths[1] +
|
||||
verts[2]->y * bary.z / vertexDepths[2]) / invZ,
|
||||
(verts[0]->z * bary.x / vertexDepths[0] +
|
||||
verts[1]->z * bary.y / vertexDepths[1] +
|
||||
verts[2]->z * bary.z / vertexDepths[2]) / invZ
|
||||
);
|
||||
|
||||
// ✅ 关键修复6:最近邻采样(最锐利)
|
||||
int ix = int(imgPt.x + 0.5f);
|
||||
int iy = int(imgPt.y + 0.5f);
|
||||
Point2f imgPt = cam.ProjectPoint(P_double);
|
||||
|
||||
// 边界检查
|
||||
if (ix < 0 || ix >= srcImg.image.width() ||
|
||||
iy < 0 || iy >= srcImg.image.height())
|
||||
if (imgPt.x < 0.5f || imgPt.y < 0.5f ||
|
||||
imgPt.x >= srcW - 0.5f || imgPt.y >= srcH - 0.5f)
|
||||
continue;
|
||||
|
||||
// 获取像素颜色(OpenCV BGR -> openMVS RGB)
|
||||
const cv::Vec3b& pixel = srcImg.image.at<cv::Vec3b>(iy, ix);
|
||||
|
||||
|
||||
// ✅ 双线性采样(关键!)
|
||||
Color color = BilinearSample(srcImg.image, imgPt);
|
||||
if (color[0] < 0 || color[1] < 0 || color[2] < 0) continue;
|
||||
|
||||
// ✅ 单一临界区:原子更新深度和颜色
|
||||
#pragma omp critical
|
||||
{
|
||||
// ✅ 直接写入,不混合
|
||||
colorBuffer(y, x) = cv::Vec3f(pixel[2], pixel[1], pixel[0]);
|
||||
validBuffer(y, x) = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// 5. 空洞填充(修复零散空白点)
|
||||
// --------------------------------------------------
|
||||
DEBUG_EXTRA("Filling holes in rasterized texture...");
|
||||
|
||||
// 使用膨胀操作填充小的空洞
|
||||
cv::Mat1b dilatedValid = validBuffer.clone();
|
||||
cv::dilate(dilatedValid, dilatedValid, cv::Mat(), cv::Point(-1,-1), 2);
|
||||
|
||||
for (int y = 0; y < textureSize; ++y) {
|
||||
for (int x = 0; x < textureSize; ++x) {
|
||||
if (validBuffer(y, x) == 0 && dilatedValid(y, x) == 1) {
|
||||
// 找到最近的有效像素
|
||||
int bestDist = INT_MAX;
|
||||
cv::Vec3f bestColor(0,0,0);
|
||||
|
||||
for (int dy = -3; dy <= 3; ++dy) {
|
||||
for (int dx = -3; dx <= 3; ++dx) {
|
||||
int ny = y + dy;
|
||||
int nx = x + dx;
|
||||
if (ny >= 0 && ny < textureSize && nx >= 0 && nx < textureSize &&
|
||||
validBuffer(ny, nx) == 1) {
|
||||
int dist = dy*dy + dx*dx;
|
||||
if (dist < bestDist) {
|
||||
bestDist = dist;
|
||||
bestColor = colorBuffer(ny, nx);
|
||||
}
|
||||
if (depth < depthBuffer(y, x)) {
|
||||
depthBuffer(y, x) = depth;
|
||||
colorBuffer(y, x) = cv::Vec3f(color[0], color[1], color[2]); // BGR->RGB
|
||||
validBuffer(y, x) = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestDist < INT_MAX) {
|
||||
colorBuffer(y, x) = bestColor;
|
||||
validBuffer(y, x) = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// 6. 最终写入纹理
|
||||
// 4. 最终写入纹理
|
||||
// --------------------------------------------------
|
||||
for (int y = 0; y < textureSize; ++y) {
|
||||
for (int x = 0; x < textureSize; ++x) {
|
||||
if (validBuffer(y, x) == 1) {
|
||||
if (validBuffer(y, x)) {
|
||||
cv::Vec3f c = colorBuffer(y, x);
|
||||
atlas(y, x) = Pixel8U{
|
||||
(unsigned char)cv::saturate_cast<uchar>(c[0]),
|
||||
|
||||
Reference in new issue
Block a user