diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index dde2b64..3a258c1 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -500,21 +500,30 @@ public: const cv::Point2f& b, const cv::Point2f& c); - void FillTextureGapsMultiView(cv::Mat& textureAtlas, - const cv::Mat1f& weightAccum, - const cv::Mat1i& sampleCount, - Pixel8U colEmpty); + void FillTextureGapsMultiView(cv::Mat& textureAtlas, + const cv::Mat1f& weightAccum, + const cv::Mat1i& sampleCount, + Pixel8U colEmpty); + int FillGapsByDistanceTransform(cv::Mat& textureAtlas, + const cv::Mat1f& weightAccum, + float maxDistance); + void AdvancedGapFilling(cv::Mat& textureAtlas, + const cv::Mat1f& weightAccum, + const cv::Mat1b& gapMask); + void FillSmallGapsWithNearest(cv::Mat& textureAtlas, + const cv::Mat1f& weightAccum, + const cv::Rect& bbox); void FillSmallGapsWithDistanceTransform(cv::Mat& textureAtlas, const cv::Mat1f& weightAccum); bool InpaintWithBlocks(cv::Mat& image, const cv::Mat1b& mask, - cv::Mat& result, int radius, int blockSize = 512); + cv::Mat& result, int radius, int blockSize); void FastAlternativeGapFilling(cv::Mat& textureAtlas, const cv::Mat1f& weightAccum, const cv::Mat1b& validMask); - void ApplySimpleColorCorrection(cv::Mat& imgMat, float strength); + void ApplySimpleColorCorrection(cv::Mat& image, float strength); - void ApplyColorCorrection(cv::Mat& image, float strength); - void ApplySharpening(Image8U3& image, float strength); + void ApplyColorCorrection(cv::Mat& image, float strength); + void ApplySharpening(cv::Mat& image, float strength); void ApplyAutoWhiteBalance(cv::Mat& image, float strength); 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); @@ -9963,51 +9972,26 @@ Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlas( } } - // 7. 第二次遍历:填充缝隙和未采样区域 - DEBUG_EXTRA("Filling gaps in texture atlas"); - FillTextureGapsMultiView(textureAtlas, weightAccum, sampleCount, colEmpty); - - // 8. 应用颜色校正和均匀化 - // ApplyColorCorrection(textureAtlas, 0.5f); // 中等强度的颜色校正 - - // 9. 应用锐化 - if (fSharpnessWeight > 0) { - // ApplySharpening(textureAtlas, fSharpnessWeight); - } + // 7. 第二次遍历:填充缝隙和未采样区域 + DEBUG_EXTRA("Filling gaps in texture atlas"); + // 将 Image8U3 转换为 cv::Mat + cv::Mat textureMat = (cv::Mat&)textureAtlas; + cv::Mat1f weightMat = weightAccum; + cv::Mat1i sampleMat = sampleCount; + FillTextureGapsMultiView(textureMat, weightMat, sampleMat, colEmpty); + + // 8. 应用颜色校正和均匀化 + // ApplyColorCorrection(textureMat, 0.5f); // 中等强度的颜色校正 + + // 9. 应用锐化 + if (fSharpnessWeight > 0) { + // ApplySharpening(textureMat, fSharpnessWeight); + } DEBUG_EXTRA("Multi-view texture atlas generation complete"); return textures; } -void MeshTexture::ApplySimpleColorCorrection(cv::Mat& imgMat, float strength) -{ - if (strength <= 0) return; - - // 应用伽马校正 - cv::Mat temp; - imgMat.convertTo(temp, CV_32FC3, 1.0/255.0); - - // 调整对比度 - cv::Mat adjusted = temp.clone(); - float alpha = 1.0f + strength; // 对比度 - float beta = -0.1f * strength; // 亮度调整 - - for (int y = 0; y < adjusted.rows; ++y) { - cv::Vec3f* row = adjusted.ptr(y); - for (int x = 0; x < adjusted.cols; ++x) { - row[x][0] = cv::saturate_cast(alpha * row[x][0] + beta); - row[x][1] = cv::saturate_cast(alpha * row[x][1] + beta); - row[x][2] = cv::saturate_cast(alpha * row[x][2] + beta); - } - } - - // 混合回原图 - cv::addWeighted(temp, 1.0f - strength, adjusted, strength, 0.0, temp); - - // 转换回8位 - temp.convertTo(imgMat, CV_8UC3, 255.0); -} - // 获取三角形内的所有像素 std::vector MeshTexture::GetPixelsInTriangle(const Point2f& pt1_, const Point2f& pt2_, @@ -10085,7 +10069,6 @@ struct InpaintProgress { std::exception_ptr exception{nullptr}; }; -// 在函数内部实现进度监控 void MeshTexture::FillTextureGapsMultiView(cv::Mat& textureAtlas, const cv::Mat1f& weightAccum, const cv::Mat1i& sampleCount, @@ -10106,7 +10089,7 @@ void MeshTexture::FillTextureGapsMultiView(cv::Mat& textureAtlas, DEBUG_EXTRA("Starting gap filling for %dx%d texture", cols, rows); - // 1. 快速创建有效掩码 + // 1. 创建有效掩码 cv::Mat1b validMask(rows, cols, (unsigned char)0); int validPixels = 0; @@ -10127,14 +10110,28 @@ void MeshTexture::FillTextureGapsMultiView(cv::Mat& textureAtlas, validPixels, validRatio * 100.0f, totalPixels - validPixels, (1.0f - validRatio) * 100.0f); - // 2. 快速形态学填充 - if (validRatio > 0.7f) { + // 2. 使用距离变换填充小缝隙 + int smallGapsFilled = FillGapsByDistanceTransform(textureAtlas, weightAccum, 10.0f); + DEBUG_EXTRA("Distance transform filled %d small gaps (radius < 10)", smallGapsFilled); + + // 3. 形态学填充扩展有效掩码 + if (validRatio > 0.5f) { // 降低阈值,更多情况下使用形态学填充 + // 更新有效掩码 + #pragma omp parallel for schedule(static) + for (int y = 0; y < rows; ++y) { + const float* weightRow = weightAccum.ptr(y); + unsigned char* maskRow = validMask.ptr(y); + for (int x = 0; x < cols; ++x) { + maskRow[x] = (weightRow[x] > 0) ? 255 : 0; + } + } + cv::Mat1b dilatedMask; cv::Mat element3 = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3, 3)); cv::dilate(validMask, dilatedMask, element3); - int smallGapsFilled = 0; - #pragma omp parallel for schedule(static) reduction(+:smallGapsFilled) + int morphologyGapsFilled = 0; + #pragma omp parallel for schedule(static) reduction(+:morphologyGapsFilled) for (int y = 0; y < rows; ++y) { const unsigned char* dilatedRow = dilatedMask.ptr(y); const unsigned char* validRow = validMask.ptr(y); @@ -10145,17 +10142,17 @@ void MeshTexture::FillTextureGapsMultiView(cv::Mat& textureAtlas, cv::Vec3b avgColor(0, 0, 0); int count = 0; - for (int dy = -1; dy <= 1 && count < 2; ++dy) { + // 搜索更大的邻域 + for (int dy = -2; dy <= 2; ++dy) { int ny = y + dy; if (ny < 0 || ny >= rows) continue; const unsigned char* neighborMaskRow = validMask.ptr(ny); const cv::Vec3b* neighborTexRow = textureAtlas.ptr(ny); - for (int dx = -1; dx <= 1 && count < 2; ++dx) { + for (int dx = -2; dx <= 2; ++dx) { int nx = x + dx; if (nx < 0 || nx >= cols) continue; - if (dx == 0 && dy == 0) continue; if (neighborMaskRow[nx]) { avgColor += neighborTexRow[nx]; @@ -10170,19 +10167,31 @@ void MeshTexture::FillTextureGapsMultiView(cv::Mat& textureAtlas, avgColor[1] / count, avgColor[2] / count ); - smallGapsFilled++; + morphologyGapsFilled++; } } } } - DEBUG_EXTRA("Fast morphology filled %d small gaps", smallGapsFilled); - dilatedMask.copyTo(validMask); + DEBUG_EXTRA("Morphology filled %d small gaps", morphologyGapsFilled); } - // 3. 识别剩余的大缝隙区域 - cv::Mat1b remainingGaps = (validMask == 0); - int remainingGapArea = cv::countNonZero(remainingGaps); + // 4. 识别剩余的大缝隙区域 + cv::Mat1b remainingGaps(rows, cols, (unsigned char)0); + int remainingGapArea = 0; + + #pragma omp parallel for schedule(static) reduction(+:remainingGapArea) + for (int y = 0; y < rows; ++y) { + const float* weightRow = weightAccum.ptr(y); + unsigned char* gapRow = remainingGaps.ptr(y); + + for (int x = 0; x < cols; ++x) { + if (weightRow[x] <= 0) { + gapRow[x] = 255; + remainingGapArea++; + } + } + } if (remainingGapArea == 0) { DEBUG_EXTRA("No remaining gaps to fill"); @@ -10192,89 +10201,326 @@ void MeshTexture::FillTextureGapsMultiView(cv::Mat& textureAtlas, DEBUG_EXTRA("Remaining gaps: %d pixels (%.2f%% of total)", remainingGapArea, static_cast(remainingGapArea) / totalPixels * 100.0f); - // 4. 使用下采样加速 - int downscaleFactor = 1; - cv::Mat downscaledAtlas, downscaledMask; - - if (cols > 2048 || rows > 2048) { - downscaleFactor = 2; - cv::resize(textureAtlas, downscaledAtlas, - cv::Size(cols / downscaleFactor, rows / downscaleFactor), - 0, 0, cv::INTER_LINEAR); - cv::resize(remainingGaps, downscaledMask, - cv::Size(cols / downscaleFactor, rows / downscaleFactor), - 0, 0, cv::INTER_NEAREST); - cv::threshold(downscaledMask, downscaledMask, 127, 255, cv::THRESH_BINARY); - - DEBUG_EXTRA("Downscaled image to %dx%d for faster processing", - cols / downscaleFactor, rows / downscaleFactor); + // 5. 分块inpaint处理大缝隙 + DEBUG_EXTRA("Starting block-based inpainting for large gaps..."); + + // 创建用于inpaint的纹理副本 + cv::Mat textureForInpaint = textureAtlas.clone(); + + // 分块inpaint + bool success = InpaintWithBlocks(textureForInpaint, remainingGaps, textureAtlas, 3, 256); + + if (!success) { + DEBUG_EXTRA("Block-based inpaint failed, using advanced alternative..."); + AdvancedGapFilling(textureAtlas, weightAccum, remainingGaps); } else { - textureAtlas.copyTo(downscaledAtlas); - remainingGaps.copyTo(downscaledMask); + DEBUG_EXTRA("Block-based inpainting completed"); + } + + // 6. 最后检查并填充剩余的任何小缝隙 + int finalGapsFilled = 0; + #pragma omp parallel for schedule(static) reduction(+:finalGapsFilled) + for (int y = 0; y < rows; ++y) { + const float* weightRow = weightAccum.ptr(y); + cv::Vec3b* textureRow = textureAtlas.ptr(y); + + for (int x = 0; x < cols; ++x) { + if (weightRow[x] <= 0) { + // 检查这个像素是否仍然是默认颜色 + cv::Vec3b current = textureRow[x]; + if (current[0] == 0 && current[1] == 0 && current[2] == 0) { + // 使用更大的邻域搜索 + cv::Vec3b avgColor(0, 0, 0); + int count = 0; + + for (int radius = 1; radius <= 5; ++radius) { + for (int dy = -radius; dy <= radius; ++dy) { + int ny = y + dy; + if (ny < 0 || ny >= rows) continue; + + const float* neighborWeightRow = weightAccum.ptr(ny); + const cv::Vec3b* neighborTexRow = textureAtlas.ptr(ny); + + for (int dx = -radius; dx <= radius; ++dx) { + int nx = x + dx; + if (nx < 0 || nx >= cols) continue; + if (dx == 0 && dy == 0) continue; + + if (neighborWeightRow[nx] > 0) { + avgColor += neighborTexRow[nx]; + count++; + } + } + } + + if (count > 0) { + textureRow[x] = cv::Vec3b( + avgColor[0] / count, + avgColor[1] / count, + avgColor[2] / count + ); + finalGapsFilled++; + break; + } + } + } + } + } + } + + if (finalGapsFilled > 0) { + DEBUG_EXTRA("Final pass filled %d remaining gaps", finalGapsFilled); } - // 5. 使用分块inpaint - int inpaintRadius = 3; - DEBUG_EXTRA("Starting block-based inpainting (TELEA algorithm, radius=%d)...", inpaintRadius); + DEBUG_EXTRA("Total gap filling time: %s", TD_TIMER_GET_FMT().c_str()); +} + +// 辅助函数:使用距离变换加速最近邻填充 +void MeshTexture::FillSmallGapsWithDistanceTransform(cv::Mat& textureAtlas, + const cv::Mat1f& weightAccum) +{ + int rows = textureAtlas.rows; + int cols = textureAtlas.cols; + + // 创建有效像素掩码 + cv::Mat1b validMask(rows, cols, (unsigned char)0); + #pragma omp parallel for schedule(static) + for (int y = 0; y < rows; ++y) { + const float* weightRow = weightAccum.ptr(y); + unsigned char* maskRow = validMask.ptr(y); + for (int x = 0; x < cols; ++x) { + maskRow[x] = (weightRow[x] > 0) ? 255 : 0; + } + } - cv::Mat inpaintedDownscaled; + // 计算距离变换 + cv::Mat distance; + cv::distanceTransform(~validMask, distance, cv::DIST_L2, cv::DIST_MASK_PRECISE, CV_32F); - // 分块处理,避免卡住 - bool success = InpaintWithBlocks(downscaledAtlas, downscaledMask, inpaintedDownscaled, inpaintRadius, 256); + // 找到最近的有效像素 + cv::Mat nearestLabels; + cv::distanceTransform(~validMask, nearestLabels, cv::DIST_L2, cv::DIST_MASK_PRECISE, CV_32S); - if (!success) { - DEBUG_EXTRA("Block-based inpaint failed, using fast alternative..."); - FastAlternativeGapFilling(textureAtlas, weightAccum, validMask); - return; + // 填充缝隙 + #pragma omp parallel for schedule(static) + for (int y = 0; y < rows; ++y) { + const float* distRow = distance.ptr(y); + const int* labelRow = nearestLabels.ptr(y); + cv::Vec3b* textureRow = textureAtlas.ptr(y); + const float* weightRow = weightAccum.ptr(y); + + for (int x = 0; x < cols; ++x) { + if (weightRow[x] <= 0 && distRow[x] > 0 && distRow[x] < 10) { // 只填充距离小于10的缝隙 + int nearestIdx = labelRow[x]; + int nearestY = nearestIdx / cols; + int nearestX = nearestIdx % cols; + + if (nearestY >= 0 && nearestY < rows && nearestX >= 0 && nearestX < cols) { + textureRow[x] = textureAtlas.at(nearestY, nearestX); + } + } + } } +} + +// 辅助函数:使用最近有效像素填充小缝隙 +void MeshTexture::FillSmallGapsWithNearest(cv::Mat& textureAtlas, + const cv::Mat1f& weightAccum, + const cv::Rect& bbox) +{ + int rows = textureAtlas.rows; + int cols = textureAtlas.cols; - DEBUG_EXTRA("Block-based inpainting completed"); + // 限制边界 + int x1 = std::max(0, bbox.x); + int y1 = std::max(0, bbox.y); + int x2 = std::min(cols, bbox.x + bbox.width); + int y2 = std::min(rows, bbox.y + bbox.height); - // 6. 上采样结果 - cv::Mat inpaintedFull; - if (downscaleFactor > 1) { - cv::resize(inpaintedDownscaled, inpaintedFull, - cv::Size(cols, rows), 0, 0, cv::INTER_LINEAR); - } else { - inpaintedDownscaled.copyTo(inpaintedFull); + // 查找区域内所有有效像素的位置 + std::vector validPoints; + for (int y = y1; y < y2; ++y) { + const float* weightRow = weightAccum.ptr(y); + for (int x = x1; x < x2; ++x) { + if (weightRow[x] > 0) { + validPoints.emplace_back(x, y); + } + } } - // 7. 填充缝隙像素 - int filledPixels = 0; - int updateInterval = std::max(1, rows / 20); - int lastProgress = -1; + if (validPoints.empty()) return; - #pragma omp parallel for schedule(static) reduction(+:filledPixels) - for (int y = 0; y < rows; ++y) { - if (y % updateInterval == 0) { - int progress = static_cast(100.0f * y / rows); - if (progress % 5 == 0 && progress != lastProgress) { - #pragma omp critical - { - if (progress != lastProgress) { - lastProgress = progress; - DEBUG_EXTRA("Gap filling progress: %d%%", progress); + // 对每个缝隙像素,使用最近的有效像素颜色 + for (int y = y1; y < y2; ++y) { + const float* weightRow = weightAccum.ptr(y); + cv::Vec3b* textureRow = textureAtlas.ptr(y); + + for (int x = x1; x < x2; ++x) { + if (weightRow[x] <= 0) { // 是缝隙像素 + // 找到最近的有效像素 + int nearestIdx = 0; + float minDist = std::numeric_limits::max(); + + for (size_t i = 0; i < validPoints.size(); ++i) { + int dx = validPoints[i].x - x; + int dy = validPoints[i].y - y; + float dist = dx*dx + dy*dy; // 平方距离 + + if (dist < minDist) { + minDist = dist; + nearestIdx = i; } } + + // 使用最近有效像素的颜色 + const cv::Point& nearest = validPoints[nearestIdx]; + textureRow[x] = textureAtlas.at(nearest.y, nearest.x); } } - - const unsigned char* gapRow = remainingGaps.ptr(y); + } +} + +// 增强的距离变换填充函数 +int MeshTexture::FillGapsByDistanceTransform(cv::Mat& textureAtlas, + const cv::Mat1f& weightAccum, + float maxDistance) +{ + int rows = textureAtlas.rows; + int cols = textureAtlas.cols; + int filledPixels = 0; + + // 创建有效像素掩码 + cv::Mat1b validMask(rows, cols, (unsigned char)0); + #pragma omp parallel for schedule(static) + for (int y = 0; y < rows; ++y) { + const float* weightRow = weightAccum.ptr(y); + unsigned char* maskRow = validMask.ptr(y); + for (int x = 0; x < cols; ++x) { + maskRow[x] = (weightRow[x] > 0) ? 255 : 0; + } + } + + // 计算距离变换 + cv::Mat distance; + cv::Mat labels; + cv::distanceTransform(~validMask, distance, cv::DIST_L2, cv::DIST_MASK_PRECISE, CV_32F); + + // 使用更精确的方法找到最近邻 + #pragma omp parallel for schedule(static) reduction(+:filledPixels) + for (int y = 0; y < rows; ++y) { + const float* distRow = distance.ptr(y); const float* weightRow = weightAccum.ptr(y); cv::Vec3b* textureRow = textureAtlas.ptr(y); - const cv::Vec3b* inpaintRow = inpaintedFull.ptr(y); for (int x = 0; x < cols; ++x) { - if (gapRow[x] && weightRow[x] <= 0) { - textureRow[x] = inpaintRow[x]; - filledPixels++; + if (weightRow[x] <= 0 && (maxDistance <= 0 || distRow[x] <= maxDistance)) { + // 查找最近的有效像素 + float minDist = FLT_MAX; + int bestY = -1, bestX = -1; + + // 搜索范围基于距离 + int searchRadius = std::min(std::max(1, static_cast(distRow[x])), 20); + int startY = std::max(0, y - searchRadius); + int endY = std::min(rows - 1, y + searchRadius); + int startX = std::max(0, x - searchRadius); + int endX = std::min(cols - 1, x + searchRadius); + + for (int ny = startY; ny <= endY; ++ny) { + const float* neighborWeightRow = weightAccum.ptr(ny); + const cv::Vec3b* neighborTexRow = textureAtlas.ptr(ny); + + for (int nx = startX; nx <= endX; ++nx) { + if (neighborWeightRow[nx] > 0) { + float dx = static_cast(nx - x); + float dy = static_cast(ny - y); + float dist = dx * dx + dy * dy; + + if (dist < minDist) { + minDist = dist; + bestY = ny; + bestX = nx; + } + } + } + } + + if (bestY != -1 && bestX != -1) { + textureRow[x] = textureAtlas.at(bestY, bestX); + filledPixels++; + } } } } - DEBUG_EXTRA("Texture gaps filled: %d pixels", filledPixels); - DEBUG_EXTRA("Total gap filling time: %s", TD_TIMER_GET_FMT().c_str()); + return filledPixels; +} + +// 高级缝隙填充方法 +void MeshTexture::AdvancedGapFilling(cv::Mat& textureAtlas, + const cv::Mat1f& weightAccum, + const cv::Mat1b& gapMask) +{ + int rows = textureAtlas.rows; + int cols = textureAtlas.cols; + int filledPixels = 0; + + // 多次迭代,逐渐扩大搜索范围 + for (int iteration = 0; iteration < 3; ++iteration) { + int iterationFilled = 0; + int searchRadius = 1 + iteration * 2; // 1, 3, 5 + + #pragma omp parallel for schedule(static) reduction(+:iterationFilled) + for (int y = 0; y < rows; ++y) { + const float* weightRow = weightAccum.ptr(y); + const unsigned char* gapRow = gapMask.ptr(y); + cv::Vec3b* textureRow = textureAtlas.ptr(y); + + for (int x = 0; x < cols; ++x) { + if (gapRow[x] == 255 && weightRow[x] <= 0) { + cv::Vec3b avgColor(0, 0, 0); + int count = 0; + + for (int dy = -searchRadius; dy <= searchRadius; ++dy) { + int ny = y + dy; + if (ny < 0 || ny >= rows) continue; + + const float* neighborWeightRow = weightAccum.ptr(ny); + const cv::Vec3b* neighborTexRow = textureAtlas.ptr(ny); + + for (int dx = -searchRadius; dx <= searchRadius; ++dx) { + int nx = x + dx; + if (nx < 0 || nx >= cols) continue; + if (dx == 0 && dy == 0) continue; + + if (neighborWeightRow[nx] > 0) { + avgColor += neighborTexRow[nx]; + count++; + } + } + } + + if (count > 0) { + textureRow[x] = cv::Vec3b( + avgColor[0] / count, + avgColor[1] / count, + avgColor[2] / count + ); + iterationFilled++; + } + } + } + } + + filledPixels += iterationFilled; + DEBUG_EXTRA("Advanced filling iteration %d: filled %d pixels (radius=%d)", + iteration + 1, iterationFilled, searchRadius); + + if (iterationFilled == 0) break; + } + + DEBUG_EXTRA("Advanced gap filling completed, total filled: %d pixels", filledPixels); } + void MeshTexture::FastAlternativeGapFilling(cv::Mat& textureAtlas, const cv::Mat1f& weightAccum, const cv::Mat1b& validMask) @@ -10354,8 +10600,6 @@ void MeshTexture::FastAlternativeGapFilling(cv::Mat& textureAtlas, DEBUG_EXTRA("Fast alternative gap filling completed"); } - -// 分块inpaint实现 bool MeshTexture::InpaintWithBlocks(cv::Mat& image, const cv::Mat1b& mask, cv::Mat& result, int radius, int blockSize) { @@ -10409,55 +10653,6 @@ bool MeshTexture::InpaintWithBlocks(cv::Mat& image, const cv::Mat1b& mask, return true; } -// 辅助函数:使用距离变换加速最近邻填充 -void MeshTexture::FillSmallGapsWithDistanceTransform(cv::Mat& textureAtlas, - const cv::Mat1f& weightAccum) -{ - int rows = textureAtlas.rows; - int cols = textureAtlas.cols; - - // 创建有效像素掩码 - cv::Mat1b validMask(rows, cols, (unsigned char)0); - #pragma omp parallel for schedule(static) - for (int y = 0; y < rows; ++y) { - const float* weightRow = weightAccum.ptr(y); - unsigned char* maskRow = validMask.ptr(y); - for (int x = 0; x < cols; ++x) { - maskRow[x] = (weightRow[x] > 0) ? 255 : 0; - } - } - - // 计算距离变换 - cv::Mat distance; - cv::distanceTransform(~validMask, distance, cv::DIST_L2, cv::DIST_MASK_PRECISE, CV_32F); - - // 找到最近的有效像素 - cv::Mat nearestLabels; - cv::distanceTransform(~validMask, nearestLabels, cv::DIST_L2, cv::DIST_MASK_PRECISE, CV_32S); - - // 填充缝隙 - #pragma omp parallel for schedule(static) - for (int y = 0; y < rows; ++y) { - const float* distRow = distance.ptr(y); - const int* labelRow = nearestLabels.ptr(y); - cv::Vec3b* textureRow = textureAtlas.ptr(y); - const float* weightRow = weightAccum.ptr(y); - - for (int x = 0; x < cols; ++x) { - if (weightRow[x] <= 0 && distRow[x] > 0 && distRow[x] < 10) { // 只填充距离小于10的缝隙 - int nearestIdx = labelRow[x]; - int nearestY = nearestIdx / cols; - int nearestX = nearestIdx % cols; - - if (nearestY >= 0 && nearestY < rows && nearestX >= 0 && nearestX < cols) { - textureRow[x] = textureAtlas.at(nearestY, nearestX); - } - } - } - } -} - -// 颜色校正函数 void MeshTexture::ApplyColorCorrection(cv::Mat& image, float strength) { if (strength <= 0) return; @@ -10465,8 +10660,6 @@ void MeshTexture::ApplyColorCorrection(cv::Mat& image, float strength) DEBUG_EXTRA("Applying color correction (strength: %.2f)", strength); TD_TIMER_START(); - cv::Mat& imgMat = (cv::Mat&)image; - if (strength < 0.3f) { // 轻微的颜色调整 ApplySimpleColorCorrection(image, strength); @@ -10475,7 +10668,7 @@ void MeshTexture::ApplyColorCorrection(cv::Mat& image, float strength) // 1. 转换为LAB颜色空间进行更自然的颜色调整 cv::Mat labImage; - cv::cvtColor(imgMat, labImage, cv::COLOR_BGR2Lab); + cv::cvtColor(image, labImage, cv::COLOR_BGR2Lab); // 分离通道 std::vector labChannels; @@ -10500,30 +10693,57 @@ void MeshTexture::ApplyColorCorrection(cv::Mat& image, float strength) cv::merge(labChannels, labImage); // 5. 转换回BGR - cv::cvtColor(labImage, imgMat, cv::COLOR_Lab2BGR); + cv::cvtColor(labImage, image, cv::COLOR_Lab2BGR); // 6. 应用自动颜色平衡 - ApplyAutoWhiteBalance(imgMat, strength * 0.5f); + ApplyAutoWhiteBalance(image, strength * 0.5f); DEBUG_EXTRA("Color correction completed in %s", TD_TIMER_GET_FMT().c_str()); } -void MeshTexture::ApplySharpening(Image8U3& image, float strength) +void MeshTexture::ApplySimpleColorCorrection(cv::Mat& image, float strength) +{ + if (strength <= 0) return; + + // 应用伽马校正 + cv::Mat temp; + image.convertTo(temp, CV_32FC3, 1.0/255.0); + + // 调整对比度 + cv::Mat adjusted = temp.clone(); + float alpha = 1.0f + strength; // 对比度 + float beta = -0.1f * strength; // 亮度调整 + + for (int y = 0; y < adjusted.rows; ++y) { + cv::Vec3f* row = adjusted.ptr(y); + for (int x = 0; x < adjusted.cols; ++x) { + row[x][0] = cv::saturate_cast(alpha * row[x][0] + beta); + row[x][1] = cv::saturate_cast(alpha * row[x][1] + beta); + row[x][2] = cv::saturate_cast(alpha * row[x][2] + beta); + } + } + + // 混合回原图 + cv::addWeighted(temp, 1.0f - strength, adjusted, strength, 0.0, temp); + + // 转换回8位 + temp.convertTo(image, CV_8UC3, 255.0); +} + +void MeshTexture::ApplySharpening(cv::Mat& image, float strength) { if (strength <= 0) return; DEBUG_EXTRA("Applying sharpening (strength: %.2f)", strength); TD_TIMER_START(); - cv::Mat& imgMat = (cv::Mat&)image; - // 1. 高斯模糊 cv::Mat blurred; - cv::GaussianBlur(imgMat, blurred, cv::Size(0, 0), 3); + cv::GaussianBlur(image, blurred, cv::Size(0, 0), 3); // 2. 计算边缘掩码 cv::Mat gray, edges; - cv::cvtColor(imgMat, gray, cv::COLOR_BGR2GRAY); + cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY); cv::Laplacian(gray, edges, CV_16S, 3); cv::convertScaleAbs(edges, edges); @@ -10532,13 +10752,13 @@ void MeshTexture::ApplySharpening(Image8U3& image, float strength) cv::threshold(edges, sharpenMask, 10, 255, cv::THRESH_BINARY); // 4. 对边缘区域应用反锐化掩蔽 - cv::Mat sharpened = imgMat.clone(); + cv::Mat sharpened = image.clone(); - for (int y = 0; y < imgMat.rows; ++y) { - for (int x = 0; x < imgMat.cols; ++x) { + for (int y = 0; y < image.rows; ++y) { + for (int x = 0; x < image.cols; ++x) { if (sharpenMask.at(y, x) > 0) { // 反锐化掩蔽公式: sharpened = original + (original - blurred) * strength - cv::Vec3b origPixel = imgMat.at(y, x); + cv::Vec3b origPixel = image.at(y, x); cv::Vec3b blurPixel = blurred.at(y, x); cv::Vec3b sharpPixel; @@ -10554,7 +10774,7 @@ void MeshTexture::ApplySharpening(Image8U3& image, float strength) } // 5. 混合锐化后的图像 - cv::addWeighted(imgMat, 1.0f - strength, sharpened, strength, 0, imgMat); + cv::addWeighted(image, 1.0f - strength, sharpened, strength, 0, image); // 6. 可选的边缘增强 if (strength > 0.5f) { @@ -10562,7 +10782,7 @@ void MeshTexture::ApplySharpening(Image8U3& image, float strength) -1, -1, -1, -1, 9, -1, -1, -1, -1); - cv::filter2D(imgMat, imgMat, -1, kernel); + cv::filter2D(image, image, -1, kernel); } DEBUG_EXTRA("Sharpening completed in %s", TD_TIMER_GET_FMT().c_str());