填色完成
This commit is contained in:
1 parent
4105129a7d
commit
992439bc44
1 file changed
+412
-192
+412
-192
@@ -500,21 +500,30 @@ public:
|
|||||||
const cv::Point2f& b,
|
const cv::Point2f& b,
|
||||||
const cv::Point2f& c);
|
const cv::Point2f& c);
|
||||||
|
|
||||||
void FillTextureGapsMultiView(cv::Mat& textureAtlas,
|
void FillTextureGapsMultiView(cv::Mat& textureAtlas,
|
||||||
const cv::Mat1f& weightAccum,
|
const cv::Mat1f& weightAccum,
|
||||||
const cv::Mat1i& sampleCount,
|
const cv::Mat1i& sampleCount,
|
||||||
Pixel8U colEmpty);
|
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,
|
void FillSmallGapsWithDistanceTransform(cv::Mat& textureAtlas,
|
||||||
const cv::Mat1f& weightAccum);
|
const cv::Mat1f& weightAccum);
|
||||||
bool InpaintWithBlocks(cv::Mat& image, const cv::Mat1b& mask,
|
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,
|
void FastAlternativeGapFilling(cv::Mat& textureAtlas,
|
||||||
const cv::Mat1f& weightAccum,
|
const cv::Mat1f& weightAccum,
|
||||||
const cv::Mat1b& validMask);
|
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 ApplyColorCorrection(cv::Mat& image, float strength);
|
||||||
void ApplySharpening(Image8U3& image, float strength);
|
void ApplySharpening(cv::Mat& image, float strength);
|
||||||
void ApplyAutoWhiteBalance(cv::Mat& image, float strength);
|
void ApplyAutoWhiteBalance(cv::Mat& image, float strength);
|
||||||
Point2f ProjectPointWithAutoCorrection(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage);
|
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);
|
Point2f ProjectPointRobust(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage, float searchRadius = 0.02f);
|
||||||
@@ -9963,51 +9972,26 @@ Mesh::Image8U3Arr MeshTexture::GenerateMultiViewTextureAtlas(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. 第二次遍历:填充缝隙和未采样区域
|
// 7. 第二次遍历:填充缝隙和未采样区域
|
||||||
DEBUG_EXTRA("Filling gaps in texture atlas");
|
DEBUG_EXTRA("Filling gaps in texture atlas");
|
||||||
FillTextureGapsMultiView(textureAtlas, weightAccum, sampleCount, colEmpty);
|
// 将 Image8U3 转换为 cv::Mat
|
||||||
|
cv::Mat textureMat = (cv::Mat&)textureAtlas;
|
||||||
|
cv::Mat1f weightMat = weightAccum;
|
||||||
|
cv::Mat1i sampleMat = sampleCount;
|
||||||
|
FillTextureGapsMultiView(textureMat, weightMat, sampleMat, colEmpty);
|
||||||
|
|
||||||
// 8. 应用颜色校正和均匀化
|
// 8. 应用颜色校正和均匀化
|
||||||
// ApplyColorCorrection(textureAtlas, 0.5f); // 中等强度的颜色校正
|
// ApplyColorCorrection(textureMat, 0.5f); // 中等强度的颜色校正
|
||||||
|
|
||||||
// 9. 应用锐化
|
// 9. 应用锐化
|
||||||
if (fSharpnessWeight > 0) {
|
if (fSharpnessWeight > 0) {
|
||||||
// ApplySharpening(textureAtlas, fSharpnessWeight);
|
// ApplySharpening(textureMat, fSharpnessWeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_EXTRA("Multi-view texture atlas generation complete");
|
DEBUG_EXTRA("Multi-view texture atlas generation complete");
|
||||||
return textures;
|
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_,
|
std::vector<cv::Point> MeshTexture::GetPixelsInTriangle(const Point2f& pt1_,
|
||||||
const Point2f& pt2_,
|
const Point2f& pt2_,
|
||||||
@@ -10085,7 +10069,6 @@ struct InpaintProgress {
|
|||||||
std::exception_ptr exception{nullptr};
|
std::exception_ptr exception{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
// 在函数内部实现进度监控
|
|
||||||
void MeshTexture::FillTextureGapsMultiView(cv::Mat& textureAtlas,
|
void MeshTexture::FillTextureGapsMultiView(cv::Mat& textureAtlas,
|
||||||
const cv::Mat1f& weightAccum,
|
const cv::Mat1f& weightAccum,
|
||||||
const cv::Mat1i& sampleCount,
|
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);
|
DEBUG_EXTRA("Starting gap filling for %dx%d texture", cols, rows);
|
||||||
|
|
||||||
// 1. 快速创建有效掩码
|
// 1. 创建有效掩码
|
||||||
cv::Mat1b validMask(rows, cols, (unsigned char)0);
|
cv::Mat1b validMask(rows, cols, (unsigned char)0);
|
||||||
int validPixels = 0;
|
int validPixels = 0;
|
||||||
|
|
||||||
@@ -10127,14 +10110,28 @@ void MeshTexture::FillTextureGapsMultiView(cv::Mat& textureAtlas,
|
|||||||
validPixels, validRatio * 100.0f,
|
validPixels, validRatio * 100.0f,
|
||||||
totalPixels - validPixels, (1.0f - validRatio) * 100.0f);
|
totalPixels - validPixels, (1.0f - validRatio) * 100.0f);
|
||||||
|
|
||||||
// 2. 快速形态学填充
|
// 2. 使用距离变换填充小缝隙
|
||||||
if (validRatio > 0.7f) {
|
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::Mat1b dilatedMask;
|
||||||
cv::Mat element3 = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3, 3));
|
cv::Mat element3 = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3, 3));
|
||||||
cv::dilate(validMask, dilatedMask, element3);
|
cv::dilate(validMask, dilatedMask, element3);
|
||||||
|
|
||||||
int smallGapsFilled = 0;
|
int morphologyGapsFilled = 0;
|
||||||
#pragma omp parallel for schedule(static) reduction(+:smallGapsFilled)
|
#pragma omp parallel for schedule(static) reduction(+:morphologyGapsFilled)
|
||||||
for (int y = 0; y < rows; ++y) {
|
for (int y = 0; y < rows; ++y) {
|
||||||
const unsigned char* dilatedRow = dilatedMask.ptr<unsigned char>(y);
|
const unsigned char* dilatedRow = dilatedMask.ptr<unsigned char>(y);
|
||||||
const unsigned char* validRow = validMask.ptr<unsigned char>(y);
|
const unsigned char* validRow = validMask.ptr<unsigned char>(y);
|
||||||
@@ -10145,17 +10142,17 @@ void MeshTexture::FillTextureGapsMultiView(cv::Mat& textureAtlas,
|
|||||||
cv::Vec3b avgColor(0, 0, 0);
|
cv::Vec3b avgColor(0, 0, 0);
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
for (int dy = -1; dy <= 1 && count < 2; ++dy) {
|
// 搜索更大的邻域
|
||||||
|
for (int dy = -2; dy <= 2; ++dy) {
|
||||||
int ny = y + dy;
|
int ny = y + dy;
|
||||||
if (ny < 0 || ny >= rows) continue;
|
if (ny < 0 || ny >= rows) continue;
|
||||||
|
|
||||||
const unsigned char* neighborMaskRow = validMask.ptr<unsigned char>(ny);
|
const unsigned char* neighborMaskRow = validMask.ptr<unsigned char>(ny);
|
||||||
const cv::Vec3b* neighborTexRow = textureAtlas.ptr<cv::Vec3b>(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;
|
int nx = x + dx;
|
||||||
if (nx < 0 || nx >= cols) continue;
|
if (nx < 0 || nx >= cols) continue;
|
||||||
if (dx == 0 && dy == 0) continue;
|
|
||||||
|
|
||||||
if (neighborMaskRow[nx]) {
|
if (neighborMaskRow[nx]) {
|
||||||
avgColor += neighborTexRow[nx];
|
avgColor += neighborTexRow[nx];
|
||||||
@@ -10170,19 +10167,31 @@ void MeshTexture::FillTextureGapsMultiView(cv::Mat& textureAtlas,
|
|||||||
avgColor[1] / count,
|
avgColor[1] / count,
|
||||||
avgColor[2] / count
|
avgColor[2] / count
|
||||||
);
|
);
|
||||||
smallGapsFilled++;
|
morphologyGapsFilled++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_EXTRA("Fast morphology filled %d small gaps", smallGapsFilled);
|
DEBUG_EXTRA("Morphology filled %d small gaps", morphologyGapsFilled);
|
||||||
dilatedMask.copyTo(validMask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 识别剩余的大缝隙区域
|
// 4. 识别剩余的大缝隙区域
|
||||||
cv::Mat1b remainingGaps = (validMask == 0);
|
cv::Mat1b remainingGaps(rows, cols, (unsigned char)0);
|
||||||
int remainingGapArea = cv::countNonZero(remainingGaps);
|
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) {
|
if (remainingGapArea == 0) {
|
||||||
DEBUG_EXTRA("No remaining gaps to fill");
|
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)",
|
DEBUG_EXTRA("Remaining gaps: %d pixels (%.2f%% of total)",
|
||||||
remainingGapArea, static_cast<float>(remainingGapArea) / totalPixels * 100.0f);
|
remainingGapArea, static_cast<float>(remainingGapArea) / totalPixels * 100.0f);
|
||||||
|
|
||||||
// 4. 使用下采样加速
|
// 5. 分块inpaint处理大缝隙
|
||||||
int downscaleFactor = 1;
|
DEBUG_EXTRA("Starting block-based inpainting for large gaps...");
|
||||||
cv::Mat downscaledAtlas, downscaledMask;
|
|
||||||
|
|
||||||
if (cols > 2048 || rows > 2048) {
|
// 创建用于inpaint的纹理副本
|
||||||
downscaleFactor = 2;
|
cv::Mat textureForInpaint = textureAtlas.clone();
|
||||||
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",
|
// 分块inpaint
|
||||||
cols / downscaleFactor, rows / downscaleFactor);
|
bool success = InpaintWithBlocks(textureForInpaint, remainingGaps, textureAtlas, 3, 256);
|
||||||
} else {
|
|
||||||
textureAtlas.copyTo(downscaledAtlas);
|
|
||||||
remainingGaps.copyTo(downscaledMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. 使用分块inpaint
|
|
||||||
int inpaintRadius = 3;
|
|
||||||
DEBUG_EXTRA("Starting block-based inpainting (TELEA algorithm, radius=%d)...", inpaintRadius);
|
|
||||||
|
|
||||||
cv::Mat inpaintedDownscaled;
|
|
||||||
|
|
||||||
// 分块处理,避免卡住
|
|
||||||
bool success = InpaintWithBlocks(downscaledAtlas, downscaledMask, inpaintedDownscaled, inpaintRadius, 256);
|
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
DEBUG_EXTRA("Block-based inpaint failed, using fast alternative...");
|
DEBUG_EXTRA("Block-based inpaint failed, using advanced alternative...");
|
||||||
FastAlternativeGapFilling(textureAtlas, weightAccum, validMask);
|
AdvancedGapFilling(textureAtlas, weightAccum, remainingGaps);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_EXTRA("Block-based inpainting completed");
|
|
||||||
|
|
||||||
// 6. 上采样结果
|
|
||||||
cv::Mat inpaintedFull;
|
|
||||||
if (downscaleFactor > 1) {
|
|
||||||
cv::resize(inpaintedDownscaled, inpaintedFull,
|
|
||||||
cv::Size(cols, rows), 0, 0, cv::INTER_LINEAR);
|
|
||||||
} else {
|
} else {
|
||||||
inpaintedDownscaled.copyTo(inpaintedFull);
|
DEBUG_EXTRA("Block-based inpainting completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. 填充缝隙像素
|
// 6. 最后检查并填充剩余的任何小缝隙
|
||||||
int filledPixels = 0;
|
int finalGapsFilled = 0;
|
||||||
int updateInterval = std::max(1, rows / 20);
|
#pragma omp parallel for schedule(static) reduction(+:finalGapsFilled)
|
||||||
int lastProgress = -1;
|
for (int y = 0; y < rows; ++y) {
|
||||||
|
const float* weightRow = weightAccum.ptr<float>(y);
|
||||||
|
cv::Vec3b* textureRow = textureAtlas.ptr<cv::Vec3b>(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<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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finalGapsFilled > 0) {
|
||||||
|
DEBUG_EXTRA("Final pass filled %d remaining gaps", finalGapsFilled);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算距离变换
|
||||||
|
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::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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 增强的距离变换填充函数
|
||||||
|
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)
|
#pragma omp parallel for schedule(static) reduction(+:filledPixels)
|
||||||
for (int y = 0; y < rows; ++y) {
|
for (int y = 0; y < rows; ++y) {
|
||||||
if (y % updateInterval == 0) {
|
const float* distRow = distance.ptr<float>(y);
|
||||||
int progress = static_cast<int>(100.0f * y / rows);
|
const float* weightRow = weightAccum.ptr<float>(y);
|
||||||
if (progress % 5 == 0 && progress != lastProgress) {
|
cv::Vec3b* textureRow = textureAtlas.ptr<cv::Vec3b>(y);
|
||||||
#pragma omp critical
|
|
||||||
{
|
for (int x = 0; x < cols; ++x) {
|
||||||
if (progress != lastProgress) {
|
if (weightRow[x] <= 0 && (maxDistance <= 0 || distRow[x] <= maxDistance)) {
|
||||||
lastProgress = progress;
|
// 查找最近的有效像素
|
||||||
DEBUG_EXTRA("Gap filling progress: %d%%", progress);
|
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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned char* gapRow = remainingGaps.ptr<unsigned char>(y);
|
filledPixels += iterationFilled;
|
||||||
const float* weightRow = weightAccum.ptr<float>(y);
|
DEBUG_EXTRA("Advanced filling iteration %d: filled %d pixels (radius=%d)",
|
||||||
cv::Vec3b* textureRow = textureAtlas.ptr<cv::Vec3b>(y);
|
iteration + 1, iterationFilled, searchRadius);
|
||||||
const cv::Vec3b* inpaintRow = inpaintedFull.ptr<cv::Vec3b>(y);
|
|
||||||
|
|
||||||
for (int x = 0; x < cols; ++x) {
|
if (iterationFilled == 0) break;
|
||||||
if (gapRow[x] && weightRow[x] <= 0) {
|
|
||||||
textureRow[x] = inpaintRow[x];
|
|
||||||
filledPixels++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_EXTRA("Texture gaps filled: %d pixels", filledPixels);
|
DEBUG_EXTRA("Advanced gap filling completed, total filled: %d pixels", filledPixels);
|
||||||
DEBUG_EXTRA("Total gap filling time: %s", TD_TIMER_GET_FMT().c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshTexture::FastAlternativeGapFilling(cv::Mat& textureAtlas,
|
void MeshTexture::FastAlternativeGapFilling(cv::Mat& textureAtlas,
|
||||||
const cv::Mat1f& weightAccum,
|
const cv::Mat1f& weightAccum,
|
||||||
const cv::Mat1b& validMask)
|
const cv::Mat1b& validMask)
|
||||||
@@ -10354,8 +10600,6 @@ void MeshTexture::FastAlternativeGapFilling(cv::Mat& textureAtlas,
|
|||||||
|
|
||||||
DEBUG_EXTRA("Fast alternative gap filling completed");
|
DEBUG_EXTRA("Fast alternative gap filling completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分块inpaint实现
|
|
||||||
bool MeshTexture::InpaintWithBlocks(cv::Mat& image, const cv::Mat1b& mask,
|
bool MeshTexture::InpaintWithBlocks(cv::Mat& image, const cv::Mat1b& mask,
|
||||||
cv::Mat& result, int radius, int blockSize)
|
cv::Mat& result, int radius, int blockSize)
|
||||||
{
|
{
|
||||||
@@ -10409,55 +10653,6 @@ bool MeshTexture::InpaintWithBlocks(cv::Mat& image, const cv::Mat1b& mask,
|
|||||||
return true;
|
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)
|
void MeshTexture::ApplyColorCorrection(cv::Mat& image, float strength)
|
||||||
{
|
{
|
||||||
if (strength <= 0) return;
|
if (strength <= 0) return;
|
||||||
@@ -10465,8 +10660,6 @@ void MeshTexture::ApplyColorCorrection(cv::Mat& image, float strength)
|
|||||||
DEBUG_EXTRA("Applying color correction (strength: %.2f)", strength);
|
DEBUG_EXTRA("Applying color correction (strength: %.2f)", strength);
|
||||||
TD_TIMER_START();
|
TD_TIMER_START();
|
||||||
|
|
||||||
cv::Mat& imgMat = (cv::Mat&)image;
|
|
||||||
|
|
||||||
if (strength < 0.3f) {
|
if (strength < 0.3f) {
|
||||||
// 轻微的颜色调整
|
// 轻微的颜色调整
|
||||||
ApplySimpleColorCorrection(image, strength);
|
ApplySimpleColorCorrection(image, strength);
|
||||||
@@ -10475,7 +10668,7 @@ void MeshTexture::ApplyColorCorrection(cv::Mat& image, float strength)
|
|||||||
|
|
||||||
// 1. 转换为LAB颜色空间进行更自然的颜色调整
|
// 1. 转换为LAB颜色空间进行更自然的颜色调整
|
||||||
cv::Mat labImage;
|
cv::Mat labImage;
|
||||||
cv::cvtColor(imgMat, labImage, cv::COLOR_BGR2Lab);
|
cv::cvtColor(image, labImage, cv::COLOR_BGR2Lab);
|
||||||
|
|
||||||
// 分离通道
|
// 分离通道
|
||||||
std::vector<cv::Mat> labChannels;
|
std::vector<cv::Mat> labChannels;
|
||||||
@@ -10500,30 +10693,57 @@ void MeshTexture::ApplyColorCorrection(cv::Mat& image, float strength)
|
|||||||
cv::merge(labChannels, labImage);
|
cv::merge(labChannels, labImage);
|
||||||
|
|
||||||
// 5. 转换回BGR
|
// 5. 转换回BGR
|
||||||
cv::cvtColor(labImage, imgMat, cv::COLOR_Lab2BGR);
|
cv::cvtColor(labImage, image, cv::COLOR_Lab2BGR);
|
||||||
|
|
||||||
// 6. 应用自动颜色平衡
|
// 6. 应用自动颜色平衡
|
||||||
ApplyAutoWhiteBalance(imgMat, strength * 0.5f);
|
ApplyAutoWhiteBalance(image, strength * 0.5f);
|
||||||
|
|
||||||
DEBUG_EXTRA("Color correction completed in %s", TD_TIMER_GET_FMT().c_str());
|
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;
|
if (strength <= 0) return;
|
||||||
|
|
||||||
DEBUG_EXTRA("Applying sharpening (strength: %.2f)", strength);
|
DEBUG_EXTRA("Applying sharpening (strength: %.2f)", strength);
|
||||||
TD_TIMER_START();
|
TD_TIMER_START();
|
||||||
|
|
||||||
cv::Mat& imgMat = (cv::Mat&)image;
|
|
||||||
|
|
||||||
// 1. 高斯模糊
|
// 1. 高斯模糊
|
||||||
cv::Mat blurred;
|
cv::Mat blurred;
|
||||||
cv::GaussianBlur(imgMat, blurred, cv::Size(0, 0), 3);
|
cv::GaussianBlur(image, blurred, cv::Size(0, 0), 3);
|
||||||
|
|
||||||
// 2. 计算边缘掩码
|
// 2. 计算边缘掩码
|
||||||
cv::Mat gray, edges;
|
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::Laplacian(gray, edges, CV_16S, 3);
|
||||||
cv::convertScaleAbs(edges, edges);
|
cv::convertScaleAbs(edges, edges);
|
||||||
|
|
||||||
@@ -10532,13 +10752,13 @@ void MeshTexture::ApplySharpening(Image8U3& image, float strength)
|
|||||||
cv::threshold(edges, sharpenMask, 10, 255, cv::THRESH_BINARY);
|
cv::threshold(edges, sharpenMask, 10, 255, cv::THRESH_BINARY);
|
||||||
|
|
||||||
// 4. 对边缘区域应用反锐化掩蔽
|
// 4. 对边缘区域应用反锐化掩蔽
|
||||||
cv::Mat sharpened = imgMat.clone();
|
cv::Mat sharpened = image.clone();
|
||||||
|
|
||||||
for (int y = 0; y < imgMat.rows; ++y) {
|
for (int y = 0; y < image.rows; ++y) {
|
||||||
for (int x = 0; x < imgMat.cols; ++x) {
|
for (int x = 0; x < image.cols; ++x) {
|
||||||
if (sharpenMask.at<uchar>(y, x) > 0) {
|
if (sharpenMask.at<uchar>(y, x) > 0) {
|
||||||
// 反锐化掩蔽公式: sharpened = original + (original - blurred) * strength
|
// 反锐化掩蔽公式: 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 blurPixel = blurred.at<cv::Vec3b>(y, x);
|
||||||
|
|
||||||
cv::Vec3b sharpPixel;
|
cv::Vec3b sharpPixel;
|
||||||
@@ -10554,7 +10774,7 @@ void MeshTexture::ApplySharpening(Image8U3& image, float strength)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 5. 混合锐化后的图像
|
// 5. 混合锐化后的图像
|
||||||
cv::addWeighted(imgMat, 1.0f - strength, sharpened, strength, 0, imgMat);
|
cv::addWeighted(image, 1.0f - strength, sharpened, strength, 0, image);
|
||||||
|
|
||||||
// 6. 可选的边缘增强
|
// 6. 可选的边缘增强
|
||||||
if (strength > 0.5f) {
|
if (strength > 0.5f) {
|
||||||
@@ -10562,7 +10782,7 @@ void MeshTexture::ApplySharpening(Image8U3& image, float strength)
|
|||||||
-1, -1, -1,
|
-1, -1, -1,
|
||||||
-1, 9, -1,
|
-1, 9, -1,
|
||||||
-1, -1, -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());
|
DEBUG_EXTRA("Sharpening completed in %s", TD_TIMER_GET_FMT().c_str());
|
||||||
|
|||||||
Reference in new issue
Block a user