Browse Source

最新优化结果

ManualUV
hesuicong 2 weeks ago
parent
commit
e555b9c804
  1. 252
      libs/MVS/SceneTexture.cpp

252
libs/MVS/SceneTexture.cpp

@ -9213,8 +9213,6 @@ void MeshTexture::GlobalSeamLeveling4()
i, components[i], texturePatches.size()); i, components[i], texturePatches.size());
} }
} }
DEBUG_EXTRA("Input validation done. seamVertices=%zu, faces=%u, vertices=%zu",
seamVertices.GetSize(), faces.GetSize(), vertices.size());
// ========== 标记无效顶点 ========== // ========== 标记无效顶点 ==========
BoolArr vertexInvalid(vertices.size()); BoolArr vertexInvalid(vertices.size());
@ -9234,7 +9232,7 @@ void MeshTexture::GlobalSeamLeveling4()
if (components[f] == NO_ID) { if (components[f] == NO_ID) {
const Face& face = faces[f]; const Face& face = faces[f];
for (int v = 0; v < 3; ++v) for (int v = 0; v < 3; ++v)
patchIndices[face[v]].idxPatch = numPatches; // dummy,后面跳过 patchIndices[face[v]].idxPatch = numPatches;
continue; continue;
} }
const uint32_t idxPatch(mapIdxPatch[components[f]]); const uint32_t idxPatch(mapIdxPatch[components[f]]);
@ -9246,13 +9244,13 @@ void MeshTexture::GlobalSeamLeveling4()
FOREACH(i, seamVertices) { FOREACH(i, seamVertices) {
const SeamVertex& seamVertex = seamVertices[i]; const SeamVertex& seamVertex = seamVertices[i];
ASSERT(!seamVertex.patches.empty()); ASSERT(!seamVertex.patches.empty());
ASSERT(seamVertex.idxVertex < vertices.size()); ASSERT(seamVertex.idxVertex < vertices.size());
PatchIndex& patchIndex = patchIndices[seamVertex.idxVertex]; PatchIndex& patchIndex = patchIndices[seamVertex.idxVertex];
patchIndex.bIndex = true; patchIndex.bIndex = true;
patchIndex.idxSeamVertex = i; patchIndex.idxSeamVertex = i;
} }
// ========== 构建 vertpatch2rows(块列号 → 连续行)========== // ========== 构建 vertpatch2rows ==========
ASSERT(vertices.size() < static_cast<VIndex>(std::numeric_limits<MatIdx>::max())); ASSERT(vertices.size() < static_cast<VIndex>(std::numeric_limits<MatIdx>::max()));
MatIdx rowsX(0); MatIdx rowsX(0);
typedef std::unordered_map<uint32_t, MatIdx> VertexPatch2RowMap; typedef std::unordered_map<uint32_t, MatIdx> VertexPatch2RowMap;
@ -9268,18 +9266,18 @@ void MeshTexture::GlobalSeamLeveling4()
ASSERT(patch.idxPatch != numPatches); ASSERT(patch.idxPatch != numPatches);
ASSERT(patch.idxPatch < numPatches); ASSERT(patch.idxPatch < numPatches);
if (vertpatch2row.find(patch.idxPatch) != vertpatch2row.end()) if (vertpatch2row.find(patch.idxPatch) != vertpatch2row.end())
continue; // 去重 continue;
vertpatch2row[patch.idxPatch] = rowsX++; vertpatch2row[patch.idxPatch] = rowsX++;
} }
} else if (patchIndex.idxPatch < numPatches) { } else if (patchIndex.idxPatch < numPatches) {
vertpatch2row[patchIndex.idxPatch] = rowsX++; vertpatch2row[patchIndex.idxPatch] = rowsX++;
} }
} }
const Eigen::Index scalarCols = (Eigen::Index)rowsX * 3; // 每块 3 个标量列 (RGB) const Eigen::Index scalarCols = (Eigen::Index)rowsX * 3;
DEBUG_EXTRA("vertpatch2rows built: rowsX(blocks)=%u, scalarCols=%lld", rowsX, (long long)scalarCols); DEBUG_EXTRA("vertpatch2rows built: rowsX=%u, scalarCols=%lld", rowsX, (long long)scalarCols);
// ============================================================ // ============================================================
// ========== 构建 Gamma(Tikhonov 正则)========== [修复1: 去重] // ========== 构建 Gamma(Tikhonov 正则)==========
// ============================================================ // ============================================================
std::vector<GSLTriplet> gammaTriplets; std::vector<GSLTriplet> gammaTriplets;
gammaTriplets.reserve((size_t)vertices.size() * 8); gammaTriplets.reserve((size_t)vertices.size() * 8);
@ -9293,7 +9291,7 @@ void MeshTexture::GlobalSeamLeveling4()
adjVerts.Empty(); adjVerts.Empty();
scene.mesh.GetAdjVertices(v, adjVerts); scene.mesh.GetAdjVertices(v, adjVerts);
const size_t MAX_ADJ = 8; // ★ 限制邻接度数,控制规模 const size_t MAX_ADJ = 8;
if (adjVerts.GetSize() > MAX_ADJ) adjVerts.Resize(MAX_ADJ); if (adjVerts.GetSize() > MAX_ADJ) adjVerts.Resize(MAX_ADJ);
VertexPatchIterator itV(patchIndices[v], seamVertices); VertexPatchIterator itV(patchIndices[v], seamVertices);
@ -9305,11 +9303,11 @@ void MeshTexture::GlobalSeamLeveling4()
const Eigen::Index colA = it_row->second; const Eigen::Index colA = it_row->second;
for (const VIndex vAdj : adjVerts) { for (const VIndex vAdj : adjVerts) {
if (v >= vAdj) continue; // ★ 无向边只处理一次(去重关键) if (v >= vAdj) continue;
VertexPatchIterator itVAdj(patchIndices[vAdj], seamVertices); VertexPatchIterator itVAdj(patchIndices[vAdj], seamVertices);
while (itVAdj.Next()) { while (itVAdj.Next()) {
const uint32_t idxPatchAdj(itVAdj); const uint32_t idxPatchAdj(itVAdj);
if (idxPatch != idxPatchAdj) continue; // 同一 patch 才正则化 if (idxPatch != idxPatchAdj) continue;
auto it_rowAdj = vertpatch2rows[vAdj].find(idxPatchAdj); auto it_rowAdj = vertpatch2rows[vAdj].find(idxPatchAdj);
if (it_rowAdj == vertpatch2rows[vAdj].end()) continue; if (it_rowAdj == vertpatch2rows[vAdj].end()) continue;
const Eigen::Index colB = it_rowAdj->second; const Eigen::Index colB = it_rowAdj->second;
@ -9321,7 +9319,6 @@ void MeshTexture::GlobalSeamLeveling4()
} }
} }
// ★ 排序 + 去重合并(消除重复邻接 → 保证矩阵性质良好)
std::sort(rawGamma.begin(), rawGamma.end(), std::sort(rawGamma.begin(), rawGamma.end(),
[](const GammaRaw& a, const GammaRaw& b) { [](const GammaRaw& a, const GammaRaw& b) {
return a.colA < b.colA || (a.colA == b.colA && a.colB < b.colB); return a.colA < b.colA || (a.colA == b.colA && a.colB < b.colB);
@ -9339,16 +9336,12 @@ void MeshTexture::GlobalSeamLeveling4()
i = j; i = j;
} }
const Eigen::Index rowsGamma = rowG; const Eigen::Index rowsGamma = rowG;
DEBUG_EXTRA("Gamma after dedup: triplets=%zu, rowsGamma=%lld, scalarCols=%lld",
gammaTriplets.size(), (long long)rowsGamma, (long long)scalarCols);
SparseMat Gamma(rowsGamma, scalarCols); SparseMat Gamma(rowsGamma, scalarCols);
Gamma.setFromTriplets(gammaTriplets.begin(), gammaTriplets.end()); Gamma.setFromTriplets(gammaTriplets.begin(), gammaTriplets.end());
Gamma.makeCompressed(); Gamma.makeCompressed();
gammaTriplets.clear(); gammaTriplets.shrink_to_fit(); gammaTriplets.clear(); gammaTriplets.shrink_to_fit();
rawGamma.clear(); rawGamma.shrink_to_fit(); rawGamma.clear(); rawGamma.shrink_to_fit();
DEBUG_EXTRA("Gamma built: %lld x %lld, nnz=%lld", (long long)Gamma.rows(),
(long long)Gamma.cols(), (long long)Gamma.nonZeros());
// ============================================================ // ============================================================
// ========== 构建矩阵 A 和 b(颜色一致性约束)========== // ========== 构建矩阵 A 和 b(颜色一致性约束)==========
@ -9373,13 +9366,26 @@ void MeshTexture::GlobalSeamLeveling4()
const TexturePatch& tpRef = texturePatches[patch0.idxPatch]; const TexturePatch& tpRef = texturePatches[patch0.idxPatch];
if (tpRef.label < 0 || tpRef.label >= (IIndex)images.size()) continue; if (tpRef.label < 0 || tpRef.label >= (IIndex)images.size()) continue;
// ★FIX: 检查 patch0.proj 是否为 NaN/Inf(崩溃根源)
if (std::isnan(patch0.proj.x) || std::isnan(patch0.proj.y) ||
std::isinf(patch0.proj.x) || std::isinf(patch0.proj.y)) {
vertexColors[i] = Color::ZERO;
continue;
}
SampleImage sampler(images[tpRef.label].image); SampleImage sampler(images[tpRef.label].image);
for (const SeamVertex::Patch::Edge& edge : patch0.edges) { for (const SeamVertex::Patch::Edge& edge : patch0.edges) {
if (edge.idxSeamVertex >= seamVertices.GetSize()) continue; // 越界保护 if (edge.idxSeamVertex >= seamVertices.GetSize()) continue;
const SeamVertex& seamVertex1 = seamVertices[edge.idxSeamVertex]; const SeamVertex& seamVertex1 = seamVertices[edge.idxSeamVertex];
const auto idxPatch1 = seamVertex1.patches.Find(patch0.idxPatch); const auto idxPatch1 = seamVertex1.patches.Find(patch0.idxPatch);
if (idxPatch1 == SeamVertex::Patches::NO_INDEX) continue; if (idxPatch1 == SeamVertex::Patches::NO_INDEX) continue;
const SeamVertex::Patch& patch1 = seamVertex1.patches[idxPatch1]; const SeamVertex::Patch& patch1 = seamVertex1.patches[idxPatch1];
// ★FIX: 检查 patch1.proj 是否为 NaN/Inf,防止 AddEdge 崩溃
if (std::isnan(patch1.proj.x) || std::isnan(patch1.proj.y) ||
std::isinf(patch1.proj.x) || std::isinf(patch1.proj.y))
continue;
sampler.AddEdge(patch0.proj, patch1.proj); sampler.AddEdge(patch0.proj, patch1.proj);
} }
vertexColors[i] = sampler.GetColor(); vertexColors[i] = sampler.GetColor();
@ -9398,185 +9404,147 @@ void MeshTexture::GlobalSeamLeveling4()
const Color& ca = vertexColors[i]; const Color& ca = vertexColors[i];
const Color& cb = vertexColors[j]; const Color& cb = vertexColors[j];
for (int c = 0; c < 3; ++c) { for (int c = 0; c < 3; ++c) {
const Eigen::Index colA = baseA * 3 + c; // ★ 标量列 = base*3 + c const Eigen::Index colA = baseA * 3 + c;
const Eigen::Index colB = baseB * 3 + c; const Eigen::Index colB = baseB * 3 + c;
ATriplets.emplace_back(rowA, colA, 1.0f); ATriplets.emplace_back(rowA, colA, 1.0f);
ATriplets.emplace_back(rowA, colB, -1.0f); ATriplets.emplace_back(rowA, colB, -1.0f);
if ((size_t)rowA >= b_coeff.size()) b_coeff.push_back(0); if ((size_t)rowA >= b_coeff.size()) b_coeff.push_back(0);
b_coeff[rowA] = cb[c] - ca[c]; // 差值方程 b_coeff[rowA] = cb[c] - ca[c];
++rowA; ++rowA;
} }
} }
} }
} }
const Eigen::Index rowsA = rowA; const Eigen::Index rowsA = rowA;
ASSERT(rowsA <= (Eigen::Index)b_coeff.size());
SparseMat A(rowsA, scalarCols); SparseMat A(rowsA, scalarCols);
A.setFromTriplets(ATriplets.begin(), ATriplets.end()); A.setFromTriplets(ATriplets.begin(), ATriplets.end());
A.makeCompressed(); A.makeCompressed();
ATriplets.clear(); ATriplets.shrink_to_fit(); ATriplets.clear(); ATriplets.shrink_to_fit();
DEBUG_EXTRA("Matrix A built: %lld constraints, scalarCols=%lld, nnz=%lld",
(long long)rowsA, (long long)scalarCols, (long long)A.nonZeros());
// ============================================================ // ============================================================
// ========== 求解:Lhs = AᵀA + λ²ΓᵀΓ,对角扰动保证正定 [修复2] // ========== 求解 ==========
// ============================================================ // ============================================================
std::vector<Color> colorAdjustments; std::vector<Color> colorAdjustments;
colorAdjustments.assign(rowsX, Color::ZERO); colorAdjustments.assign(rowsX, Color::ZERO);
if (rowsA > 0 && rowsX > 0) { if (rowsA > 0 && rowsX > 0) {
// ★ Lhs = AᵀA + λ² ΓᵀΓ
SparseMat ATA = A.transpose() * A; SparseMat ATA = A.transpose() * A;
SparseMat GTG = Gamma.transpose() * Gamma; SparseMat GTG = Gamma.transpose() * Gamma;
const float lambda = 0.5f; // ★ 正则化强度(可调) const float lambda = 0.1f;
GTG *= lambda * lambda; GTG *= lambda * lambda;
ASSERT(ATA.rows() == GTG.rows() && ATA.cols() == GTG.cols()); // 尺寸一致性检查
ATA += GTG; ATA += GTG;
SparseMat& Lhs = ATA; SparseMat& Lhs = ATA;
// ★★★ 对角加扰动,严格保证正定 ★★★ const float eps = 1e-3f;
const float eps = 1e-3f; // 足够小不影响结果,保证正定
for (Eigen::Index i = 0; i < (Eigen::Index)Lhs.rows(); ++i) { for (Eigen::Index i = 0; i < (Eigen::Index)Lhs.rows(); ++i) {
Lhs.coeffRef(i, i) += eps; Lhs.coeffRef(i, i) += eps;
} }
DEBUG_EXTRA("Lhs built: %lld x %lld, nnz=%lld (diag += %g)",
(long long)Lhs.rows(), (long long)Lhs.cols(),
(long long)Lhs.nonZeros(), eps);
// ★ 用 ConjugateGradient(只需对称,不要求严格 SPD)
Eigen::ConjugateGradient<SparseMat, Eigen::Lower> solver; Eigen::ConjugateGradient<SparseMat, Eigen::Lower> solver;
solver.setMaxIterations(2000); solver.setMaxIterations(2000);
solver.setTolerance(0.001f); solver.setTolerance(0.001f);
solver.compute(Lhs); solver.compute(Lhs);
if (solver.info() != Eigen::Success) { if (solver.info() == Eigen::Success) {
DEBUG_EXTRA("WARN: Lhs compute failed (info=%d), skipping solve", (int)solver.info());
} else {
// ★ b 与通道无关 → 一次求解,结果按 InnerStride<3> 分通道
Eigen::VectorXf b(rowsA); Eigen::VectorXf b(rowsA);
for (Eigen::Index k = 0; k < rowsA; ++k) b(k) = b_coeff[k]; for (Eigen::Index k = 0; k < rowsA; ++k) b(k) = b_coeff[k];
const Eigen::VectorXf rhs = A.transpose() * b; const Eigen::VectorXf rhs = A.transpose() * b;
const Eigen::VectorXf x = solver.solve(rhs); const Eigen::VectorXf x = solver.solve(rhs);
DEBUG_EXTRA("solve: %d iterations, residual=%g", solver.iterations(), solver.error());
if (solver.info() == Eigen::Success && (Eigen::Index)(x.size()) >= scalarCols) {
// ★ 从 x 提取 colorAdjustments(每块一个 Color,显式循环避免对齐问题) colorAdjustments.assign(rowsX, Color::ZERO);
if (solver.info() == Eigen::Success && (Eigen::Index)(x.size()) >= scalarCols) { for (Eigen::Index v = 0; v < (Eigen::Index)rowsX; ++v) {
colorAdjustments.assign(rowsX, Color::ZERO); const float r = x(v * 3 + 0);
for (Eigen::Index v = 0; v < (Eigen::Index)rowsX; ++v) { const float g = x(v * 3 + 1);
const float r = x(v * 3 + 0); const float b_ch = x(v * 3 + 2);
const float g = x(v * 3 + 1);
const float b_ch = x(v * 3 + 2); // ★ 恢复原版逻辑:计算均值并减去(消除亮度偏移,只保留色度调整)
const float mean = (r + g + b_ch) / 3.0f; const float mean = (r + g + b_ch) / 3.0f;
// colorAdjustments[v] = Color(r, g, b_ch); colorAdjustments[v] = Color(r - mean, g - mean, b_ch - mean);
colorAdjustments[v] = Color(r - mean, g - mean, b_ch - mean); }
} }
} else {
DEBUG_EXTRA("WARN: solve not successful, using zero adjustments");
}
} }
} else {
DEBUG_EXTRA("WARN: no constraints (rowsA=%lld), skip solve", (long long)rowsA);
} }
DEBUG_EXTRA("solve done. colorAdjustments: %zux3", rowsX); b_coeff.clear(); b_coeff.shrink_to_fit();
// ========== 应用颜色修正(输出到 correctedPatchImages,不碰 images)========== // ========== 应用颜色修正 ==========
correctedPatchImages.resize(numPatches); correctedPatchImages.resize(numPatches);
#ifdef TEXOPT_USE_OPENMP #ifdef TEXOPT_USE_OPENMP
#pragma omp parallel for schedule(dynamic) #pragma omp parallel for schedule(dynamic)
#endif #endif
for (int i = 0; i < (int)numPatches; ++i) { for (int i = 0; i < (int)numPatches; ++i) {
const uint32_t idxPatch = (uint32_t)i; const uint32_t idxPatch = (uint32_t)i;
const TexturePatch& texturePatch = texturePatches[idxPatch]; const TexturePatch& texturePatch = texturePatches[idxPatch];
if (texturePatch.label < 0 || texturePatch.label >= (int)images.size()) continue; if (texturePatch.label < 0 || texturePatch.label >= (int)images.size()) continue;
if (texturePatch.rect.width <= 0 || texturePatch.rect.height <= 0) continue; if (texturePatch.rect.width <= 0 || texturePatch.rect.height <= 0) continue;
// ★ 从原始 images 读(只读,不修改) const cv::Mat& srcROI = images[texturePatch.label].image(texturePatch.rect);
const cv::Mat& srcROI = images[texturePatch.label].image(texturePatch.rect); if (srcROI.empty()) continue;
if (srcROI.empty()) continue;
// ★ 创建独立输出 buffer cv::Mat& outPatch = correctedPatchImages[idxPatch];
cv::Mat& outPatch = correctedPatchImages[idxPatch]; outPatch = cv::Mat::zeros(texturePatch.rect.size(), CV_8UC3);
outPatch = cv::Mat::zeros(texturePatch.rect.size(), CV_8UC3);
ColorMap imageAdj(texturePatch.rect.size()); ColorMap imageAdj(texturePatch.rect.size());
imageAdj.memset(0); imageAdj.memset(0);
struct RasterPatch { struct RasterPatch {
const TexCoord* tri; const TexCoord* tri;
Color colors[3]; Color colors[3];
ColorMap& image; ColorMap& image;
RasterPatch(ColorMap& _image) : image(_image) {} RasterPatch(ColorMap& _image) : image(_image) {}
inline cv::Size Size() const { return image.size(); } inline cv::Size Size() const { return image.size(); }
void operator()(const ImageRef& pt, const Point3f& bary) { void operator()(const ImageRef& pt, const Point3f& bary) {
image(pt) = colors[0]*bary.x + colors[1]*bary.y + colors[2]*bary.z; Color c = colors[0]*bary.x + colors[1]*bary.y + colors[2]*bary.z;
// ★ 过滤 NaN/Inf 调整值
if (std::isnan(c[0]) || std::isnan(c[1]) || std::isnan(c[2]) ||
std::isinf(c[0]) || std::isinf(c[1]) || std::isinf(c[2])) {
return;
}
image(pt) = c;
} }
} data(imageAdj); } data(imageAdj);
ASSERT(texturePatch.faces.size() < 1000000); // 合理范围
for (const FIndex idxFace : texturePatch.faces) {
if (idxFace >= faces.GetSize()) continue;
const Face& face = faces[idxFace];
ASSERT(idxFace * 3 + 2 < faceTexcoords.GetSize()); for (const FIndex idxFace : texturePatch.faces) {
for (int v = 0; v < 3; ++v) { if (idxFace >= faces.GetSize()) continue;
ASSERT(face[v] < vertices.size()); const Face& face = faces[idxFace];
ASSERT(face[v] < vertpatch2rows.size()); data.tri = faceTexcoords.Begin() + idxFace * 3;
}
// // ★ UV [0,1] → patch 局部像素坐标 for (int v = 0; v < 3; ++v) {
// const cv::Size patchSize = texturePatch.rect.size(); ASSERT(face[v] < vertices.size());
// TexCoord localTri[3]; auto search = vertpatch2rows[face[v]].find(idxPatch);
// const TexCoord* uvTri = faceTexcoords.Begin() + idxFace * 3; if (search != vertpatch2rows[face[v]].end()) {
// for (int v = 0; v < 3; ++v) { ASSERT(search->second < (Eigen::Index)colorAdjustments.size());
// localTri[v] = TexCoord(uvTri[v].x * patchSize.width, data.colors[v] = colorAdjustments[search->second];
// uvTri[v].y * patchSize.height); } else {
// } data.colors[v] = Color::ZERO;
// data.tri = localTri; }
// 改回直接用,不乘 }
data.tri = faceTexcoords.Begin() + idxFace * 3; ColorMap::RasterizeTriangleBary(data.tri[0], data.tri[1], data.tri[2], data);
}
for (int v = 0; v < 3; ++v) { // ★FIX: 改回 DilateMean<1>(和原版一致,之前 <5> 过度模糊)
ASSERT(face[v] < vertices.size()); imageAdj.DilateMean<1>(imageAdj, Color::ZERO);
auto search = vertpatch2rows[face[v]].find(idxPatch);
if (search != vertpatch2rows[face[v]].end())
{
ASSERT(search->second < (Eigen::Index)colorAdjustments.size());
data.colors[v] = colorAdjustments[search->second];
}
else
data.colors[v] = Color::ZERO;
}
ColorMap::RasterizeTriangleBary(data.tri[0], data.tri[1], data.tri[2], data);
}
imageAdj.DilateMean<5>(imageAdj, Color::ZERO);
// ★ 把修正叠加到 outPatch(从 srcROI 读原始颜色)
for (int r = 0; r < srcROI.rows; ++r) {
for (int c = 0; c < srcROI.cols; ++c) {
const Color& a = imageAdj(r, c);
if (a == Color::ZERO) {
outPatch.at<cv::Vec3b>(r, c) = srcROI.at<cv::Vec3b>(r, c);
continue;
}
const Pixel8U& v = srcROI.at<Pixel8U>(r, c);
const Color col(RGB2YCBCR(Color(v)));
// const Color acol(YCBCR2RGB(Color(col + a))); // ★FIX: 去掉 GAIN=10,Y/Cb/Cr 全部 1:1 微调(和原版 GSL3 一致)
const float GAIN = 10.0f; // ★ 增益,可调(5~20) for (int r = 0; r < srcROI.rows; ++r) {
const Color acol(YCBCR2RGB(Color( for (int c = 0; c < srcROI.cols; ++c) {
col[0] + a[0] * 1.0f, // Y 微调 const Color& a = imageAdj(r, c);
col[1] + a[1] * GAIN, // Cb 大幅调 if (a == Color::ZERO) {
col[2] + a[2] * GAIN // Cr 大幅调 outPatch.at<cv::Vec3b>(r, c) = srcROI.at<cv::Vec3b>(r, c);
))); continue;
}
outPatch.at<cv::Vec3b>(r, c) = cv::Vec3b( const Pixel8U& v = srcROI.at<Pixel8U>(r, c);
(uint8_t)CLAMP(ROUND2INT(acol[0]), 0, 255), const Color col(RGB2YCBCR(Color(v)));
(uint8_t)CLAMP(ROUND2INT(acol[1]), 0, 255), // ★ 直接加,不放大任何通道
(uint8_t)CLAMP(ROUND2INT(acol[2]), 0, 255)); const Color acol(YCBCR2RGB(Color(col + a)));
} outPatch.at<cv::Vec3b>(r, c) = cv::Vec3b(
} (uint8_t)CLAMP(ROUND2INT(acol[0]), 0, 255),
} (uint8_t)CLAMP(ROUND2INT(acol[1]), 0, 255),
DEBUG_EXTRA("All patch adjustments applied (to local buffers)"); (uint8_t)CLAMP(ROUND2INT(acol[2]), 0, 255));
}
}
}
DEBUG_EXTRA("GlobalSeamLeveling4 finished successfully."); DEBUG_EXTRA("GlobalSeamLeveling4 finished successfully.");
} }

Loading…
Cancel
Save