From c38456c7b48340b33992f3de3fcfd828d2a74ac2 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Wed, 29 Apr 2026 09:49:19 +0800 Subject: [PATCH] =?UTF-8?q?=E9=80=9A=E8=BF=87=E7=BC=96=E8=AF=91=EF=BC=8C?= =?UTF-8?q?=E4=BD=86=E6=98=AF=E6=9C=89=E7=99=BD=E5=B9=B3=E8=A1=A1=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 154 +++++++++++++++++++++++++++----------- 1 file changed, 110 insertions(+), 44 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index e3e1fa7..6aa64ac 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -601,6 +601,7 @@ public: Pixel8U colEmpty, float fSharpnessWeight ); + cv::Vec3b ConvertBGRtoRGBIfNeeded(const cv::Vec3b& bgrColor); Point2f ProjectPointWithAutoCorrection(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage); Point2f ProjectPointRobust(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage, float searchRadius = 0.02f); bool ValidateProjection(const Vertex& worldPoint, const Image& sourceImage, Point2f imgPoint, float maxReprojectionError = 1.5f); @@ -13737,10 +13738,18 @@ Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasWith3DBridge( const float uvHeight = uvBounds.ptMax.y() - uvBounds.ptMin.y(); const int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple); -// 创建目标纹理 + // 创建目标纹理 Mesh::Image8U3Arr textures; Image8U3& textureAtlas = textures.emplace_back(textureSize, textureSize); - // 注意:cv::Scalar 使用 BGR 顺序 + + // 修正1: 明确颜色通道顺序 + // Pixel8U通常是RGB顺序,OpenCV使用BGR,我们需要保持一致 + // 这里明确指定RGB顺序 + DEBUG_EXTRA("设置背景色: RGB(%d,%d,%d) -> BGR(%d,%d,%d)", + colEmpty.r, colEmpty.g, colEmpty.b, + colEmpty.b, colEmpty.g, colEmpty.r); + + // 使用BGR顺序(OpenCV默认) textureAtlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); DEBUG_EXTRA("生成纹理图集: 尺寸=%dx%d, UV范围=[%.3f,%.3f]-[%.3f,%.3f]", @@ -13748,16 +13757,29 @@ Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasWith3DBridge( uvBounds.ptMin.x(), uvBounds.ptMin.y(), uvBounds.ptMax.x(), uvBounds.ptMax.y()); - // 添加调试信息 - DEBUG_EXTRA("colEmpty: R=%d, G=%d, B=%d", colEmpty.r, colEmpty.g, colEmpty.b); - DEBUG_EXTRA("OpenCV图像通道顺序: BGR"); + // 添加颜色通道调试 + DEBUG_EXTRA("颜色通道配置:"); + DEBUG_EXTRA(" - colEmpty: R=%d, G=%d, B=%d", colEmpty.r, colEmpty.g, colEmpty.b); + DEBUG_EXTRA(" - OpenCV通道顺序: BGR"); - // 检查颜色通道顺序 + // 检查源纹理颜色通道顺序 if (!sourceTextures.empty()) { const Image8U3& firstTex = sourceTextures[0]; - cv::Vec3b firstPixel = firstTex.at(0, 0); - DEBUG_EXTRA("源纹理第一个像素: B=%d, G=%d, R=%d", - firstPixel[0], firstPixel[1], firstPixel[2]); + if (!firstTex.empty()) { + cv::Vec3b firstPixel = firstTex.at(0, 0); + DEBUG_EXTRA("源纹理[0]第一个像素: B=%d, G=%d, R=%d (OpenCV BGR顺序)", + firstPixel[0], firstPixel[1], firstPixel[2]); + } + } + + // 检查images中的颜色通道 + if (!images.empty()) { + const Image& firstImg = images[0]; + if (!firstImg.image.empty()) { + cv::Vec3b firstPixel = firstImg.image.at(0, 0); + DEBUG_EXTRA("images[0]第一个像素: B=%d, G=%d, R=%d (OpenCV BGR顺序)", + firstPixel[0], firstPixel[1], firstPixel[2]); + } } // 3. 统计信息 @@ -13787,17 +13809,7 @@ Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasWith3DBridge( // 获取面的几何信息 const TexCoord* meshUVs = &scene.mesh.faceTexcoords[faceID * 3]; const Face& face = scene.mesh.faces[faceID]; - - // 获取源纹理信息 - const TexCoord* srcUVs = &sourceTexcoords[faceID * 3]; - const TexIndex textureIdx = sourceTexindices.empty() ? 0 : sourceTexindices[faceID]; - - if (textureIdx >= sourceTextures.size()) { - failedFaces++; - continue; - } - - const Image8U3& sourceTexture = sourceTextures[textureIdx]; + const Image& sourceImage = images[idxView]; // 计算面的UV边界 AABB2f faceBounds(true); @@ -13841,19 +13853,23 @@ Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasWith3DBridge( vertices[face[1]] * barycentric.y + vertices[face[2]] * barycentric.z; - // 方案A:从原始图像采样 - Point2f imgPoint = ProjectPointWithAutoCorrection(images[idxView].camera, worldPoint, images[idxView]); + // 从原始图像采样 + Point2f imgPoint = sourceImage.camera.ProjectPointP(worldPoint); + + // 确保在图像范围内 + imgPoint.x = CLAMP(imgPoint.x, 0.0f, (float)(sourceImage.image.cols - 1)); + imgPoint.y = CLAMP(imgPoint.y, 0.0f, (float)(sourceImage.image.rows - 1)); - if (imgPoint.x < 0 || imgPoint.x >= images[idxView].image.cols || - imgPoint.y < 0 || imgPoint.y >= images[idxView].image.rows) { + if (imgPoint.x < 0 || imgPoint.x >= sourceImage.image.cols || + imgPoint.y < 0 || imgPoint.y >= sourceImage.image.rows) { continue; } - // 修正:使用双线性插值采样 - const int x0 = (int)floor(imgPoint.x); - const int y0 = (int)floor(imgPoint.y); - const int x1 = std::min(x0 + 1, images[idxView].image.cols - 1); - const int y1 = std::min(y0 + 1, images[idxView].image.rows - 1); + // 双线性插值采样 + const int x0 = (int)imgPoint.x; + const int y0 = (int)imgPoint.y; + const int x1 = std::min(x0 + 1, sourceImage.image.cols - 1); + const int y1 = std::min(y0 + 1, sourceImage.image.rows - 1); const float fx = imgPoint.x - x0; const float fy = imgPoint.y - y0; @@ -13861,30 +13877,39 @@ Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasWith3DBridge( const float fy1 = 1.0f - fy; // 采样四个点的颜色 - const cv::Vec3b& c00 = images[idxView].image.at(y0, x0); - const cv::Vec3b& c01 = images[idxView].image.at(y0, x1); - const cv::Vec3b& c10 = images[idxView].image.at(y1, x0); - const cv::Vec3b& c11 = images[idxView].image.at(y1, x1); + const cv::Vec3b& c00 = sourceImage.image.at(y0, x0); + const cv::Vec3b& c01 = sourceImage.image.at(y0, x1); + const cv::Vec3b& c10 = sourceImage.image.at(y1, x0); + const cv::Vec3b& c11 = sourceImage.image.at(y1, x1); // 双线性插值 - const cv::Vec3b color = + cv::Vec3b sampledColor = c00 * (fx1 * fy1) + c01 * (fx * fy1) + c10 * (fx1 * fy) + c11 * (fx * fy); - // 注意:OpenCV是BGR顺序,而Pixel8U通常是RGB顺序 - // 这里我们需要确保颜色通道正确 + // 修正2: 统一颜色通道处理 + // 方法1: 保持BGR顺序(与OpenCV一致) + // 如果最终显示为RGB,则需要在显示时转换 + + // 方法2: 转换为RGB顺序存储 + cv::Vec3b finalColor = ConvertBGRtoRGBIfNeeded(sampledColor); + #ifdef _USE_OPENMP #pragma omp critical #endif { - // 方案1:直接使用BGR顺序(OpenCV默认) - // textureAtlas.at(y, x) = color; + // 使用转换后的颜色 + textureAtlas.at(y, x) = finalColor; - // 方案2:交换B和R通道(如果颜色偏蓝,说明B和R反了) - // 将BGR转换为RGB - textureAtlas.at(y, x) = cv::Vec3b(color[2], color[1], color[0]); + // 调试:记录前几个像素的颜色 + if (processedFaces == 0 && faceSampledPixels < 3) { + DEBUG_EXTRA("像素[%d,%d] 颜色: 源图像BGR=(%d,%d,%d), 目标RGB=(%d,%d,%d)", + x, y, + sampledColor[0], sampledColor[1], sampledColor[2], + finalColor[0], finalColor[1], finalColor[2]); + } } faceSampledPixels++; @@ -13904,17 +13929,58 @@ Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasWith3DBridge( // 5. 填充空洞 if (processedFaces > 0 && sampledPixels > 0) { - // FillTextureGaps(textureAtlas, scene.mesh.faceTexcoords, (FIndex)scene.mesh.faces.size(), textureSize, colEmpty); + // FillTextureGapsWithColorCorrection(textureAtlas, scene.mesh.faceTexcoords, + // (FIndex)scene.mesh.faces.size(), textureSize, colEmpty); } // 6. 锐化处理 - if (fSharpnessWeight > 0) { - // ApplySharpening(textureAtlas, fSharpnessWeight); + if (fSharpnessWeight > 0 && sampledPixels > 0) { + // ApplySharpeningWithColorBalance(textureAtlas, fSharpnessWeight); + } + + // 7. 最终颜色检查 + if (!textureAtlas.empty()) { + cv::Scalar mean = cv::mean(textureAtlas); + DEBUG_EXTRA("最终纹理平均颜色: B=%.1f, G=%.1f, R=%.1f", + mean[0], mean[1], mean[2]); + + // 检查颜色通道 + int rgbCount[3] = {0, 0, 0}; + int totalSamples = 0; + + for (int y = 0; y < textureAtlas.rows && y < 10; y++) { + for (int x = 0; x < textureAtlas.cols && x < 10; x++) { + cv::Vec3b pixel = textureAtlas.at(y, x); + if (pixel != cv::Vec3b(colEmpty.b, colEmpty.g, colEmpty.r)) { + rgbCount[0] += pixel[0]; + rgbCount[1] += pixel[1]; + rgbCount[2] += pixel[2]; + totalSamples++; + } + } + } + + if (totalSamples > 0) { + DEBUG_EXTRA("前10x10像素平均颜色: B=%d, G=%d, R=%d", + rgbCount[0]/totalSamples, rgbCount[1]/totalSamples, rgbCount[2]/totalSamples); + } } return textures; } +cv::Vec3b MeshTexture::ConvertBGRtoRGBIfNeeded(const cv::Vec3b& bgrColor) { + // 根据配置决定是否转换 + // 如果OpenCV图像是BGR顺序,而我们希望存储为RGB,则交换B和R + static bool bConvertBGRtoRGB = true; // 可根据需要调整 + + if (bConvertBGRtoRGB) { + return cv::Vec3b(bgrColor[2], bgrColor[1], bgrColor[0]); // BGR->RGB + } else { + return bgrColor; // 保持BGR + } +} + Point2f MeshTexture::ProjectPointWithAutoCorrection(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage) { Point2f imgPoint = camera.ProjectPointP(worldPoint);