|
|
|
@ -584,6 +584,16 @@ struct MeshTexture { |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
std::vector<VirtualFaceGeometry> m_virtualFaceGeometries; |
|
|
|
std::vector<VirtualFaceGeometry> m_virtualFaceGeometries; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
// 面片级全局增益(Global Gain)数据结构
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
struct FaceGainInfo { |
|
|
|
|
|
|
|
float gain; // 亮度增益
|
|
|
|
|
|
|
|
bool valid; // 是否有效(有相邻视角对比)
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
// 面片级增益数组(索引对应 face ID)
|
|
|
|
|
|
|
|
std::vector<FaceGainInfo> m_faceGains; |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
public: |
|
|
|
MeshTexture(Scene& _scene, unsigned _nResolutionLevel=0, unsigned _nMinResolution=640); |
|
|
|
MeshTexture(Scene& _scene, unsigned _nResolutionLevel=0, unsigned _nMinResolution=640); |
|
|
|
~MeshTexture(); |
|
|
|
~MeshTexture(); |
|
|
|
@ -718,6 +728,25 @@ public: |
|
|
|
const VirtualFace& vf, |
|
|
|
const VirtualFace& vf, |
|
|
|
IIndex viewID, |
|
|
|
IIndex viewID, |
|
|
|
VirtualFaceGeometry& geom); |
|
|
|
VirtualFaceGeometry& geom); |
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
// ComputeGlobalGain - 计算面片级全局增益
|
|
|
|
|
|
|
|
// 功能:对每个面片,比较其主视角与备选视角的平均亮度,
|
|
|
|
|
|
|
|
// 计算增益使两者对齐。仅调整明暗,不改变色相。
|
|
|
|
|
|
|
|
// 参数:virtualFaceViews 即光栅化循环中的 virtualFaceViews
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
void ComputeGlobalGain( |
|
|
|
|
|
|
|
const Scene& scene, |
|
|
|
|
|
|
|
const Mesh& mesh, |
|
|
|
|
|
|
|
const std::vector<std::vector<IIndex>>& virtualFaceViews); |
|
|
|
|
|
|
|
float SampleLuminanceBilinear(const cv::Mat& img, float x, float y); |
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
// GetFaceMeanLuminance - 获取面片在指定视角下的平均亮度
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
float GetFaceMeanLuminance( |
|
|
|
|
|
|
|
const Scene& scene, |
|
|
|
|
|
|
|
const Mesh& mesh, |
|
|
|
|
|
|
|
int faceID, |
|
|
|
|
|
|
|
int viewID) const; |
|
|
|
|
|
|
|
|
|
|
|
std::vector<std::vector<IIndex>> faceViews; |
|
|
|
std::vector<std::vector<IIndex>> faceViews; |
|
|
|
std::vector<std::vector<float>> faceViewWeights; |
|
|
|
std::vector<std::vector<float>> faceViewWeights; |
|
|
|
@ -16606,6 +16635,17 @@ bool MeshTexture::RasterizeVirtualFaces( |
|
|
|
|
|
|
|
|
|
|
|
if (!m_gainsEstimated) EstimateGlobalPhotometricCorrection(); |
|
|
|
if (!m_gainsEstimated) EstimateGlobalPhotometricCorrection(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 面片级全局增益(Global Gain)=====
|
|
|
|
|
|
|
|
if (m_faceGains.empty()) { |
|
|
|
|
|
|
|
VERBOSE("Computing global gains for %zu faces...", virtualFaceViews.size()); |
|
|
|
|
|
|
|
ComputeGlobalGain(scene, scene.mesh, virtualFaceViews); |
|
|
|
|
|
|
|
VERBOSE("Global gains computed: %zu valid, %zu invalid", |
|
|
|
|
|
|
|
std::count_if(m_faceGains.begin(), m_faceGains.end(), |
|
|
|
|
|
|
|
[](const FaceGainInfo& fg) { return fg.valid; }), |
|
|
|
|
|
|
|
std::count_if(m_faceGains.begin(), m_faceGains.end(), |
|
|
|
|
|
|
|
[](const FaceGainInfo& fg) { return !fg.valid; })); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
VERBOSE("[Raster] Step 2: starting patch loop, total faces=%zu", virtualFaceMap.size()); |
|
|
|
VERBOSE("[Raster] Step 2: starting patch loop, total faces=%zu", virtualFaceMap.size()); |
|
|
|
|
|
|
|
|
|
|
|
// 3. 逐三角形光栅化
|
|
|
|
// 3. 逐三角形光栅化
|
|
|
|
@ -16705,7 +16745,19 @@ bool MeshTexture::RasterizeVirtualFaces( |
|
|
|
size_t idx = atlasY * textureSize + atlasX; |
|
|
|
size_t idx = atlasY * textureSize + atlasX; |
|
|
|
|
|
|
|
|
|
|
|
if (currentScore > m_texelScores[idx].score) { |
|
|
|
if (currentScore > m_texelScores[idx].score) { |
|
|
|
// 原样写入,不做任何混合
|
|
|
|
// ★ 应用面片级全局增益(仅调整亮度,不改变色相)
|
|
|
|
|
|
|
|
float gain = 1.0f; |
|
|
|
|
|
|
|
if (i >= 0 && i < (int)m_faceGains.size() && m_faceGains[i].valid) { |
|
|
|
|
|
|
|
gain = m_faceGains[i].gain; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (gain != 1.0f) { |
|
|
|
|
|
|
|
color[0] = cv::saturate_cast<uchar>(color[0] * gain); // B
|
|
|
|
|
|
|
|
color[1] = cv::saturate_cast<uchar>(color[1] * gain); // G
|
|
|
|
|
|
|
|
color[2] = cv::saturate_cast<uchar>(color[2] * gain); // R
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 写入图集
|
|
|
|
atlas.at<cv::Vec3b>(atlasY, atlasX) = color; |
|
|
|
atlas.at<cv::Vec3b>(atlasY, atlasX) = color; |
|
|
|
m_texelScores[idx].score = currentScore; |
|
|
|
m_texelScores[idx].score = currentScore; |
|
|
|
m_texelScores[idx].viewID = viewID; |
|
|
|
m_texelScores[idx].viewID = viewID; |
|
|
|
@ -16861,11 +16913,32 @@ bool MeshTexture::RasterizeVirtualFaces( |
|
|
|
|
|
|
|
|
|
|
|
cv::Vec3b original = smoothSrc.at<cv::Vec3b>(y, x); |
|
|
|
cv::Vec3b original = smoothSrc.at<cv::Vec3b>(y, x); |
|
|
|
|
|
|
|
|
|
|
|
// 混合:50% 原始 + 50% 邻居平均
|
|
|
|
// 计算到本patch内部像素的最小欧氏距离
|
|
|
|
|
|
|
|
float minDistSq = 1e10f; |
|
|
|
|
|
|
|
for (int dy = -5; dy <= 5; ++dy) { |
|
|
|
|
|
|
|
for (int dx = -5; dx <= 5; ++dx) { |
|
|
|
|
|
|
|
int nx = x + dx, ny = y + dy; |
|
|
|
|
|
|
|
if (nx < 0 || nx >= textureSize || ny < 0 || ny >= textureSize) continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
size_t nidx = ny * textureSize + nx; |
|
|
|
|
|
|
|
if (m_texelPatchID[nidx] == centerPatch) { |
|
|
|
|
|
|
|
float distSq = (float)(dx*dx + dy*dy); |
|
|
|
|
|
|
|
minDistSq = std::min(minDistSq, distSq); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float minDist = std::sqrt(minDistSq); |
|
|
|
|
|
|
|
float sigma = 2.0f; |
|
|
|
|
|
|
|
float weight = std::exp(-minDist * minDist / (2.0f * sigma * sigma)); |
|
|
|
|
|
|
|
weight = 1.0f - weight; |
|
|
|
|
|
|
|
weight = std::max(0.1f, std::min(0.5f, weight)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 应用混合
|
|
|
|
atlas.at<cv::Vec3b>(y, x) = cv::Vec3b( |
|
|
|
atlas.at<cv::Vec3b>(y, x) = cv::Vec3b( |
|
|
|
cv::saturate_cast<uchar>(original[0] * 0.5f + avg[0] * 0.5f), |
|
|
|
cv::saturate_cast<uchar>(original[0] * (1-weight) + avg[0] * weight), |
|
|
|
cv::saturate_cast<uchar>(original[1] * 0.5f + avg[1] * 0.5f), |
|
|
|
cv::saturate_cast<uchar>(original[1] * (1-weight) + avg[1] * weight), |
|
|
|
cv::saturate_cast<uchar>(original[2] * 0.5f + avg[2] * 0.5f) |
|
|
|
cv::saturate_cast<uchar>(original[2] * (1-weight) + avg[2] * weight) |
|
|
|
); |
|
|
|
); |
|
|
|
seamPixelCount++; |
|
|
|
seamPixelCount++; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -19156,6 +19229,69 @@ float MeshTexture::PointToLineDistance(const Point2f& p, const Point2f& a, const |
|
|
|
float disty = p.y - projy; |
|
|
|
float disty = p.y - projy; |
|
|
|
return std::sqrt(distx*distx + disty*disty); |
|
|
|
return std::sqrt(distx*distx + disty*disty); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
// GetFaceMeanLuminance - 获取面片在指定视角下的平均亮度
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
float MeshTexture::GetFaceMeanLuminance( |
|
|
|
|
|
|
|
const Scene& scene, |
|
|
|
|
|
|
|
const Mesh& mesh, |
|
|
|
|
|
|
|
int faceID, |
|
|
|
|
|
|
|
int viewID) const |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (viewID < 0 || viewID >= (int)scene.images.size()) return 0.0f; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const Image8U3& image = scene.images[viewID].image; |
|
|
|
|
|
|
|
if (image.empty()) return 0.0f; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取面片顶点(直接索引)
|
|
|
|
|
|
|
|
Point3f v0 = mesh.vertices[faceID * 3]; |
|
|
|
|
|
|
|
Point3f v1 = mesh.vertices[faceID * 3 + 1]; |
|
|
|
|
|
|
|
Point3f v2 = mesh.vertices[faceID * 3 + 2]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取相机参数
|
|
|
|
|
|
|
|
const Camera& camera = scene.images[viewID].camera; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ★ 修复:显式转为 Point3d(double)再投影
|
|
|
|
|
|
|
|
// 世界坐标 → 相机坐标
|
|
|
|
|
|
|
|
Point3d c0 = camera.TransformPointW2C(Point3d(v0)); |
|
|
|
|
|
|
|
Point3d c1 = camera.TransformPointW2C(Point3d(v1)); |
|
|
|
|
|
|
|
Point3d c2 = camera.TransformPointW2C(Point3d(v2)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否在相机前方
|
|
|
|
|
|
|
|
if (c0.z <= 0 || c1.z <= 0 || c2.z <= 0) return 0.0f; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 相机坐标 → 图像坐标(归一化)
|
|
|
|
|
|
|
|
Point2f p0 = camera.TransformPointC2I(c0); |
|
|
|
|
|
|
|
Point2f p1 = camera.TransformPointC2I(c1); |
|
|
|
|
|
|
|
Point2f p2 = camera.TransformPointC2I(c2); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算面片在图像上的包围盒
|
|
|
|
|
|
|
|
int minX = std::max(0, (int)std::floor(std::min({p0.x, p1.x, p2.x}))); |
|
|
|
|
|
|
|
int maxX = std::min(image.width() - 1, (int)std::ceil(std::max({p0.x, p1.x, p2.x}))); |
|
|
|
|
|
|
|
int minY = std::max(0, (int)std::floor(std::min({p0.y, p1.y, p2.y}))); |
|
|
|
|
|
|
|
int maxY = std::min(image.height() - 1, (int)std::ceil(std::max({p0.y, p1.y, p2.y}))); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (minX >= maxX || minY >= maxY) return 0.0f; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 采样包围盒内的像素,计算平均亮度
|
|
|
|
|
|
|
|
float totalLum = 0.0f; |
|
|
|
|
|
|
|
int count = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int y = minY; y <= maxY; ++y) { |
|
|
|
|
|
|
|
for (int x = minX; x <= maxX; ++x) { |
|
|
|
|
|
|
|
const Pixel8U& pixel = image(y, x); |
|
|
|
|
|
|
|
// 跳过纯黑(无效区域)
|
|
|
|
|
|
|
|
if (pixel.r == 0 && pixel.g == 0 && pixel.b == 0) continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算亮度(BT.709 标准)
|
|
|
|
|
|
|
|
float lum = 0.2126f * pixel.r + 0.7152f * pixel.g + 0.0722f * pixel.b; |
|
|
|
|
|
|
|
totalLum += lum; |
|
|
|
|
|
|
|
count++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (count > 0) ? (totalLum / count) : 0.0f; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
// ============================================================
|
|
|
|
// 2. 核心:计算单应矩阵 / 仿射矩阵
|
|
|
|
// 2. 核心:计算单应矩阵 / 仿射矩阵
|
|
|
|
@ -19265,6 +19401,154 @@ bool MeshTexture::ComputeHomographyForVirtualFace( |
|
|
|
return geom.isValid; |
|
|
|
return geom.isValid; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 辅助函数:双线性插值采样亮度
|
|
|
|
|
|
|
|
float MeshTexture::SampleLuminanceBilinear(const cv::Mat& img, float x, float y) { |
|
|
|
|
|
|
|
int ix = (int)x; |
|
|
|
|
|
|
|
int iy = (int)y; |
|
|
|
|
|
|
|
float fx = x - ix; |
|
|
|
|
|
|
|
float fy = y - iy; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const uchar* p00 = img.ptr<uchar>(iy, ix); |
|
|
|
|
|
|
|
const uchar* p10 = img.ptr<uchar>(iy, ix + 1); |
|
|
|
|
|
|
|
const uchar* p01 = img.ptr<uchar>(iy + 1, ix); |
|
|
|
|
|
|
|
const uchar* p11 = img.ptr<uchar>(iy + 1, ix + 1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float c00 = 0.299f * p00[2] + 0.587f * p00[1] + 0.114f * p00[0]; |
|
|
|
|
|
|
|
float c10 = 0.299f * p10[2] + 0.587f * p10[1] + 0.114f * p10[0]; |
|
|
|
|
|
|
|
float c01 = 0.299f * p01[2] + 0.587f * p01[1] + 0.114f * p01[0]; |
|
|
|
|
|
|
|
float c11 = 0.299f * p11[2] + 0.587f * p11[1] + 0.114f * p11[0]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float c0 = c00 * (1-fx) + c10 * fx; |
|
|
|
|
|
|
|
float c1 = c01 * (1-fx) + c11 * fx; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return c0 * (1-fy) + c1 * fy; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
// ComputeGlobalGain - 计算面片级全局增益
|
|
|
|
|
|
|
|
// 功能:对每个面片,比较其主视角与备选视角的平均亮度,
|
|
|
|
|
|
|
|
// 计算增益使两者对齐。仅调整明暗,不改变色相。
|
|
|
|
|
|
|
|
// 参数:virtualFaceViews 即光栅化循环中的 virtualFaceViews
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
void MeshTexture::ComputeGlobalGain( |
|
|
|
|
|
|
|
const Scene& scene, |
|
|
|
|
|
|
|
const Mesh& mesh, |
|
|
|
|
|
|
|
const std::vector<std::vector<IIndex>>& virtualFaceViews) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
VERBOSE("[GlobalGain] Starting fast computation for %zu faces...", virtualFaceViews.size()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const size_t numFaces = virtualFaceViews.size(); |
|
|
|
|
|
|
|
m_faceGains.resize(numFaces); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int progressInterval = std::max(1, (int)numFaces / 100); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pragma omp parallel for schedule(dynamic, 1000) |
|
|
|
|
|
|
|
for (int faceID = 0; faceID < (int)numFaces; ++faceID) { |
|
|
|
|
|
|
|
if (faceID % progressInterval == 0) { |
|
|
|
|
|
|
|
#pragma omp critical |
|
|
|
|
|
|
|
VERBOSE("[GlobalGain] Progress: %d/%zu (%.1f%%)", |
|
|
|
|
|
|
|
faceID, numFaces, 100.0 * faceID / numFaces); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const auto& views = virtualFaceViews[faceID]; |
|
|
|
|
|
|
|
if (views.size() < 2) { |
|
|
|
|
|
|
|
m_faceGains[faceID].valid = false; |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 只采样三角形三个顶点和中心共4个点
|
|
|
|
|
|
|
|
float lumMain = 0.0f; |
|
|
|
|
|
|
|
float lumAlt = 0.0f; |
|
|
|
|
|
|
|
int sampleCount = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取三角形的三个顶点
|
|
|
|
|
|
|
|
const Point3f& v0f = mesh.vertices[faceID * 3]; |
|
|
|
|
|
|
|
const Point3f& v1f = mesh.vertices[faceID * 3 + 1]; |
|
|
|
|
|
|
|
const Point3f& v2f = mesh.vertices[faceID * 3 + 2]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 转为 Point3d(因为 Camera::TransformPointW2C 内部用 double)
|
|
|
|
|
|
|
|
Point3d v0(v0f.x, v0f.y, v0f.z); |
|
|
|
|
|
|
|
Point3d v1(v1f.x, v1f.y, v1f.z); |
|
|
|
|
|
|
|
Point3d v2(v2f.x, v2f.y, v2f.z); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 采样点:三个顶点 + 中心(都用 Point3d)
|
|
|
|
|
|
|
|
Point3d samplePoints[4] = { |
|
|
|
|
|
|
|
v0, v1, v2, |
|
|
|
|
|
|
|
Point3d((v0.x + v1.x + v2.x) / 3.0, |
|
|
|
|
|
|
|
(v0.y + v1.y + v2.y) / 3.0, |
|
|
|
|
|
|
|
(v0.z + v1.z + v2.z) / 3.0) |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int s = 0; s < 4; ++s) { |
|
|
|
|
|
|
|
const Point3d& pt = samplePoints[s]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 主视角采样
|
|
|
|
|
|
|
|
int mainViewID = views[0]; |
|
|
|
|
|
|
|
const Camera& mainCam = scene.images[mainViewID].camera; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Point3d ptCam = mainCam.TransformPointW2C(pt); |
|
|
|
|
|
|
|
if (ptCam.z <= 0) continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Point2f ptImg = mainCam.TransformPointC2I(ptCam); |
|
|
|
|
|
|
|
if (ptImg.x < 0 || ptImg.x >= scene.images[mainViewID].image.cols - 1 || |
|
|
|
|
|
|
|
ptImg.y < 0 || ptImg.y >= scene.images[mainViewID].image.rows - 1) continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 双线性插值采样主视角亮度
|
|
|
|
|
|
|
|
float lum = SampleLuminanceBilinear(scene.images[mainViewID].image, ptImg.x, ptImg.y); |
|
|
|
|
|
|
|
if (lum < 5.0f) continue; // 跳过纯黑
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 备选视角采样(取第一个有效的)
|
|
|
|
|
|
|
|
float lumAltSample = 0.0f; |
|
|
|
|
|
|
|
bool foundAlt = false; |
|
|
|
|
|
|
|
for (size_t v = 1; v < views.size(); ++v) { |
|
|
|
|
|
|
|
int altViewID = views[v]; |
|
|
|
|
|
|
|
const Camera& altCam = scene.images[altViewID].camera; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Point3d altPtCam = altCam.TransformPointW2C(pt); |
|
|
|
|
|
|
|
if (altPtCam.z <= 0) continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Point2f altPtImg = altCam.TransformPointC2I(altPtCam); |
|
|
|
|
|
|
|
if (altPtImg.x < 0 || altPtImg.x >= scene.images[altViewID].image.cols - 1 || |
|
|
|
|
|
|
|
altPtImg.y < 0 || altPtImg.y >= scene.images[altViewID].image.rows - 1) continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lumAltSample = SampleLuminanceBilinear(scene.images[altViewID].image, altPtImg.x, altPtImg.y); |
|
|
|
|
|
|
|
if (lumAltSample > 5.0f) { |
|
|
|
|
|
|
|
foundAlt = true; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (foundAlt) { |
|
|
|
|
|
|
|
lumMain += lum; |
|
|
|
|
|
|
|
lumAlt += lumAltSample; |
|
|
|
|
|
|
|
sampleCount++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (sampleCount > 0) { |
|
|
|
|
|
|
|
float avgMain = lumMain / sampleCount; |
|
|
|
|
|
|
|
float avgAlt = lumAlt / sampleCount; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (avgMain > 1.0f && avgAlt > 1.0f) { |
|
|
|
|
|
|
|
float gain = avgAlt / avgMain; |
|
|
|
|
|
|
|
m_faceGains[faceID].gain = std::max(0.25f, std::min(4.0f, gain)); |
|
|
|
|
|
|
|
m_faceGains[faceID].valid = true; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
m_faceGains[faceID].valid = false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
m_faceGains[faceID].valid = false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int validCount = 0; |
|
|
|
|
|
|
|
for (const auto& fg : m_faceGains) { |
|
|
|
|
|
|
|
if (fg.valid) validCount++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VERBOSE("[GlobalGain] Done. Valid: %d/%zu", validCount, numFaces); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool MeshTexture::GenerateTextureWithVirtualFacesInternal(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, |
|
|
|
bool MeshTexture::GenerateTextureWithVirtualFacesInternal(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, |
|
|
|
unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, |
|
|
|
unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, |
|
|
|
Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, |
|
|
|
Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, |
|
|
|
|