|
|
|
|
@ -500,21 +500,30 @@ public:
@@ -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(
@@ -9963,51 +9972,26 @@ Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlas(
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 7. 第二次遍历:填充缝隙和未采样区域
|
|
|
|
|
DEBUG_EXTRA("Filling gaps in texture atlas"); |
|
|
|
|
FillTextureGapsMultiView(textureAtlas, weightAccum, sampleCount, colEmpty); |
|
|
|
|
// 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(textureAtlas, 0.5f); // 中等强度的颜色校正
|
|
|
|
|
// 8. 应用颜色校正和均匀化
|
|
|
|
|
// ApplyColorCorrection(textureMat, 0.5f); // 中等强度的颜色校正
|
|
|
|
|
|
|
|
|
|
// 9. 应用锐化
|
|
|
|
|
if (fSharpnessWeight > 0) { |
|
|
|
|
// ApplySharpening(textureAtlas, fSharpnessWeight);
|
|
|
|
|
} |
|
|
|
|
// 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<cv::Vec3f>(y); |
|
|
|
|
for (int x = 0; x < adjusted.cols; ++x) { |
|
|
|
|
row[x][0] = cv::saturate_cast<float>(alpha * row[x][0] + beta); |
|
|
|
|
row[x][1] = cv::saturate_cast<float>(alpha * row[x][1] + beta); |
|
|
|
|
row[x][2] = cv::saturate_cast<float>(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<cv::Point> MeshTexture::GetPixelsInTriangle(const Point2f& pt1_, |
|
|
|
|
const Point2f& pt2_, |
|
|
|
|
@ -10085,7 +10069,6 @@ struct InpaintProgress {
@@ -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,
@@ -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,
@@ -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<float>(y); |
|
|
|
|
unsigned char* maskRow = validMask.ptr<unsigned char>(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<unsigned char>(y); |
|
|
|
|
const unsigned char* validRow = validMask.ptr<unsigned char>(y); |
|
|
|
|
@ -10145,17 +10142,17 @@ void MeshTexture::FillTextureGapsMultiView(cv::Mat& textureAtlas,
@@ -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<unsigned char>(ny); |
|
|
|
|
const cv::Vec3b* neighborTexRow = textureAtlas.ptr<cv::Vec3b>(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,
@@ -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<float>(y); |
|
|
|
|
unsigned char* gapRow = remainingGaps.ptr<unsigned char>(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,
@@ -10192,89 +10201,326 @@ void MeshTexture::FillTextureGapsMultiView(cv::Mat& textureAtlas,
|
|
|
|
|
DEBUG_EXTRA("Remaining gaps: %d pixels (%.2f%% of total)", |
|
|
|
|
remainingGapArea, static_cast<float>(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"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 5. 使用分块inpaint
|
|
|
|
|
int inpaintRadius = 3; |
|
|
|
|
DEBUG_EXTRA("Starting block-based inpainting (TELEA algorithm, radius=%d)...", inpaintRadius); |
|
|
|
|
// 6. 最后检查并填充剩余的任何小缝隙
|
|
|
|
|
int finalGapsFilled = 0; |
|
|
|
|
#pragma omp parallel for schedule(static) reduction(+:finalGapsFilled) |
|
|
|
|
for (int y = 0; y < rows; ++y) { |
|
|
|
|
const float* weightRow = weightAccum.ptr<float>(y); |
|
|
|
|
cv::Vec3b* textureRow = textureAtlas.ptr<cv::Vec3b>(y); |
|
|
|
|
|
|
|
|
|
cv::Mat inpaintedDownscaled; |
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
// 分块处理,避免卡住
|
|
|
|
|
bool success = InpaintWithBlocks(downscaledAtlas, downscaledMask, inpaintedDownscaled, inpaintRadius, 256); |
|
|
|
|
for (int radius = 1; radius <= 5; ++radius) { |
|
|
|
|
for (int dy = -radius; dy <= radius; ++dy) { |
|
|
|
|
int ny = y + dy; |
|
|
|
|
if (ny < 0 || ny >= rows) continue; |
|
|
|
|
|
|
|
|
|
if (!success) { |
|
|
|
|
DEBUG_EXTRA("Block-based inpaint failed, using fast alternative..."); |
|
|
|
|
FastAlternativeGapFilling(textureAtlas, weightAccum, validMask); |
|
|
|
|
return; |
|
|
|
|
const float* neighborWeightRow = weightAccum.ptr<float>(ny); |
|
|
|
|
const cv::Vec3b* neighborTexRow = textureAtlas.ptr<cv::Vec3b>(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; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Block-based inpainting completed"); |
|
|
|
|
if (finalGapsFilled > 0) { |
|
|
|
|
DEBUG_EXTRA("Final pass filled %d remaining gaps", finalGapsFilled); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 6. 上采样结果
|
|
|
|
|
cv::Mat inpaintedFull; |
|
|
|
|
if (downscaleFactor > 1) { |
|
|
|
|
cv::resize(inpaintedDownscaled, inpaintedFull, |
|
|
|
|
cv::Size(cols, rows), 0, 0, cv::INTER_LINEAR); |
|
|
|
|
} else { |
|
|
|
|
inpaintedDownscaled.copyTo(inpaintedFull); |
|
|
|
|
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<float>(y); |
|
|
|
|
unsigned char* maskRow = validMask.ptr<unsigned char>(y); |
|
|
|
|
for (int x = 0; x < cols; ++x) { |
|
|
|
|
maskRow[x] = (weightRow[x] > 0) ? 255 : 0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 7. 填充缝隙像素
|
|
|
|
|
int filledPixels = 0; |
|
|
|
|
int updateInterval = std::max(1, rows / 20); |
|
|
|
|
int lastProgress = -1; |
|
|
|
|
// 计算距离变换
|
|
|
|
|
cv::Mat distance; |
|
|
|
|
cv::distanceTransform(~validMask, distance, cv::DIST_L2, cv::DIST_MASK_PRECISE, CV_32F); |
|
|
|
|
|
|
|
|
|
#pragma omp parallel for schedule(static) reduction(+:filledPixels) |
|
|
|
|
// 找到最近的有效像素
|
|
|
|
|
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) { |
|
|
|
|
if (y % updateInterval == 0) { |
|
|
|
|
int progress = static_cast<int>(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); |
|
|
|
|
const float* distRow = distance.ptr<float>(y); |
|
|
|
|
const int* labelRow = nearestLabels.ptr<int>(y); |
|
|
|
|
cv::Vec3b* textureRow = textureAtlas.ptr<cv::Vec3b>(y); |
|
|
|
|
const float* weightRow = weightAccum.ptr<float>(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<cv::Vec3b>(nearestY, nearestX); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 辅助函数:使用最近有效像素填充小缝隙
|
|
|
|
|
void MeshTexture::FillSmallGapsWithNearest(cv::Mat& textureAtlas, |
|
|
|
|
const cv::Mat1f& weightAccum, |
|
|
|
|
const cv::Rect& bbox) |
|
|
|
|
{ |
|
|
|
|
int rows = textureAtlas.rows; |
|
|
|
|
int cols = textureAtlas.cols; |
|
|
|
|
|
|
|
|
|
// 限制边界
|
|
|
|
|
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); |
|
|
|
|
|
|
|
|
|
// 查找区域内所有有效像素的位置
|
|
|
|
|
std::vector<cv::Point> validPoints; |
|
|
|
|
for (int y = y1; y < y2; ++y) { |
|
|
|
|
const float* weightRow = weightAccum.ptr<float>(y); |
|
|
|
|
for (int x = x1; x < x2; ++x) { |
|
|
|
|
if (weightRow[x] > 0) { |
|
|
|
|
validPoints.emplace_back(x, y); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (validPoints.empty()) return; |
|
|
|
|
|
|
|
|
|
// 对每个缝隙像素,使用最近的有效像素颜色
|
|
|
|
|
for (int y = y1; y < y2; ++y) { |
|
|
|
|
const float* weightRow = weightAccum.ptr<float>(y); |
|
|
|
|
cv::Vec3b* textureRow = textureAtlas.ptr<cv::Vec3b>(y); |
|
|
|
|
|
|
|
|
|
for (int x = x1; x < x2; ++x) { |
|
|
|
|
if (weightRow[x] <= 0) { // 是缝隙像素
|
|
|
|
|
// 找到最近的有效像素
|
|
|
|
|
int nearestIdx = 0; |
|
|
|
|
float minDist = std::numeric_limits<float>::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<cv::Vec3b>(nearest.y, nearest.x); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const unsigned char* gapRow = remainingGaps.ptr<unsigned char>(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<float>(y); |
|
|
|
|
unsigned char* maskRow = validMask.ptr<unsigned char>(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<float>(y); |
|
|
|
|
const float* weightRow = weightAccum.ptr<float>(y); |
|
|
|
|
cv::Vec3b* textureRow = textureAtlas.ptr<cv::Vec3b>(y); |
|
|
|
|
const cv::Vec3b* inpaintRow = inpaintedFull.ptr<cv::Vec3b>(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<int>(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<float>(ny); |
|
|
|
|
const cv::Vec3b* neighborTexRow = textureAtlas.ptr<cv::Vec3b>(ny); |
|
|
|
|
|
|
|
|
|
for (int nx = startX; nx <= endX; ++nx) { |
|
|
|
|
if (neighborWeightRow[nx] > 0) { |
|
|
|
|
float dx = static_cast<float>(nx - x); |
|
|
|
|
float dy = static_cast<float>(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<cv::Vec3b>(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<float>(y); |
|
|
|
|
const unsigned char* gapRow = gapMask.ptr<unsigned char>(y); |
|
|
|
|
cv::Vec3b* textureRow = textureAtlas.ptr<cv::Vec3b>(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<float>(ny); |
|
|
|
|
const cv::Vec3b* neighborTexRow = textureAtlas.ptr<cv::Vec3b>(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,
@@ -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,
@@ -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<float>(y); |
|
|
|
|
unsigned char* maskRow = validMask.ptr<unsigned char>(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<float>(y); |
|
|
|
|
const int* labelRow = nearestLabels.ptr<int>(y); |
|
|
|
|
cv::Vec3b* textureRow = textureAtlas.ptr<cv::Vec3b>(y); |
|
|
|
|
const float* weightRow = weightAccum.ptr<float>(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<cv::Vec3b>(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)
@@ -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)
@@ -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<cv::Mat> labChannels; |
|
|
|
|
@ -10500,30 +10693,57 @@ void MeshTexture::ApplyColorCorrection(cv::Mat& image, float strength)
@@ -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<cv::Vec3f>(y); |
|
|
|
|
for (int x = 0; x < adjusted.cols; ++x) { |
|
|
|
|
row[x][0] = cv::saturate_cast<float>(alpha * row[x][0] + beta); |
|
|
|
|
row[x][1] = cv::saturate_cast<float>(alpha * row[x][1] + beta); |
|
|
|
|
row[x][2] = cv::saturate_cast<float>(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)
@@ -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<uchar>(y, x) > 0) { |
|
|
|
|
// 反锐化掩蔽公式: sharpened = original + (original - blurred) * strength
|
|
|
|
|
cv::Vec3b origPixel = imgMat.at<cv::Vec3b>(y, x); |
|
|
|
|
cv::Vec3b origPixel = image.at<cv::Vec3b>(y, x); |
|
|
|
|
cv::Vec3b blurPixel = blurred.at<cv::Vec3b>(y, x); |
|
|
|
|
|
|
|
|
|
cv::Vec3b sharpPixel; |
|
|
|
|
@ -10554,7 +10774,7 @@ void MeshTexture::ApplySharpening(Image8U3& image, float strength)
@@ -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)
@@ -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()); |
|
|
|
|
|