优化眼镜填空
This commit is contained in:
1 parent
6f7805c78d
commit
38c0b9948a
3 files changed
+2011
-274
No files matched your search
+827
-181
File diff suppressed because it is too large.
Load diff
@@ -58,6 +58,7 @@ class FillMeshGenerator:
|
|||||||
if in_idx:
|
if in_idx:
|
||||||
self.IN_IDX = in_idx
|
self.IN_IDX = in_idx
|
||||||
self.report = []
|
self.report = []
|
||||||
|
self.lens_mask = None
|
||||||
|
|
||||||
def log(self, s):
|
def log(self, s):
|
||||||
"""日志记录"""
|
"""日志记录"""
|
||||||
@@ -401,110 +402,153 @@ class FillMeshGenerator:
|
|||||||
|
|
||||||
return gz1, gz2, fz, bz, have_g1, have_g2, have_f, have_b, g1f, W, H
|
return gz1, gz2, fz, bz, have_g1, have_g2, have_f, have_b, g1f, W, H
|
||||||
|
|
||||||
# ======================================================================
|
|
||||||
# 步骤 3: 构建足迹 mask 与填充区域 R
|
|
||||||
# ======================================================================
|
|
||||||
def build_footprint(self, g1f):
|
def build_footprint(self, g1f):
|
||||||
"""构建眼镜足迹 mask 和填充区域 R"""
|
"""构建眼镜足迹 mask 和填充区域 R"""
|
||||||
self.log("== 3. 眼镜足迹 / 填充区域 R ==")
|
self.log("== 3. 眼镜足迹 / 填充区域 R ==")
|
||||||
mask = g1f.copy()
|
self.lens_mask = None # <-- 新增: 初始化
|
||||||
|
mask = g1f.copy()
|
||||||
|
|
||||||
# 取主分量
|
# 取主分量
|
||||||
mask, n_stray = self.largest_component_8(mask)
|
mask, n_stray = self.largest_component_8(mask)
|
||||||
self.log(f" 足迹取主分量: 剔除杂散块 {n_stray} 格 (误标到镜框外的皮肤/后脑)")
|
self.log(f" 足迹取主分量: 剔除杂散块 {n_stray} 格 (误标到镜框外的皮肤/后脑)")
|
||||||
|
|
||||||
# 中央区剪除(鼻侧多余填充)
|
# 中央区剪除(鼻侧多余填充)
|
||||||
holes = ~mask & ~self.flood_outside(mask)
|
holes = ~mask & ~self.flood_outside(mask)
|
||||||
hl = np.zeros(mask.shape, np.int32)
|
hl = np.zeros(mask.shape, np.int32)
|
||||||
big_holes = []
|
big_holes = []
|
||||||
for j in range(mask.shape[0]):
|
for j in range(mask.shape[0]):
|
||||||
for i in range(mask.shape[1]):
|
for i in range(mask.shape[1]):
|
||||||
if holes[j, i] and hl[j, i] == 0:
|
if holes[j, i] and hl[j, i] == 0:
|
||||||
hl[j, i] = 1
|
hl[j, i] = 1
|
||||||
q = deque([(j, i)])
|
q = deque([(j, i)])
|
||||||
pts = [(j, i)]
|
pts = [(j, i)]
|
||||||
while q:
|
while q:
|
||||||
y, x = q.popleft()
|
y, x = q.popleft()
|
||||||
for dy, dx in ((1,0),(-1,0),(0,1),(0,-1),
|
for dy, dx in ((1,0),(-1,0),(0,1),(0,-1),
|
||||||
(1,1),(1,-1),(-1,1),(-1,-1)):
|
(1,1),(1,-1),(-1,1),(-1,-1)):
|
||||||
ny, nx = y + dy, x + dx
|
ny, nx = y + dy, x + dx
|
||||||
if (0 <= ny < mask.shape[0] and 0 <= nx < mask.shape[1]
|
if (0 <= ny < mask.shape[0] and 0 <= nx < mask.shape[1]
|
||||||
and holes[ny, nx] and hl[ny, nx] == 0):
|
and holes[ny, nx] and hl[ny, nx] == 0):
|
||||||
hl[ny, nx] = 1
|
hl[ny, nx] = 1
|
||||||
q.append((ny, nx))
|
q.append((ny, nx))
|
||||||
pts.append((ny, nx))
|
pts.append((ny, nx))
|
||||||
if len(pts) >= 500:
|
if len(pts) >= 500:
|
||||||
big_holes.append(pts)
|
big_holes.append(pts)
|
||||||
|
|
||||||
if len(big_holes) >= 2:
|
if len(big_holes) >= 2:
|
||||||
big_holes.sort(key=len, reverse=True)
|
big_holes.sort(key=len, reverse=True)
|
||||||
h_l, h_r = sorted(big_holes[:2],
|
h_l, h_r = sorted(big_holes[:2],
|
||||||
key=lambda pts: sum(p[1] for p in pts) / len(pts))
|
key=lambda pts: sum(p[1] for p in pts) / len(pts))
|
||||||
bl = (min(p[1] for p in h_l), max(p[1] for p in h_l), min(p[0] for p in h_l))
|
bl = (min(p[1] for p in h_l), max(p[1] for p in h_l), min(p[0] for p in h_l))
|
||||||
br = (min(p[1] for p in h_r), max(p[1] for p in h_r), min(p[0] for p in h_r))
|
br = (min(p[1] for p in h_r), max(p[1] for p in h_r), min(p[0] for p in h_r))
|
||||||
x_in_l, x_in_r = bl[1], br[0]
|
x_in_l, x_in_r = bl[1], br[0]
|
||||||
y_cut = min(bl[2], br[2])
|
y_cut = min(bl[2], br[2])
|
||||||
if x_in_l < x_in_r:
|
if x_in_l < x_in_r:
|
||||||
cut = np.zeros_like(mask, bool)
|
cut = np.zeros_like(mask, bool)
|
||||||
cut[:y_cut, x_in_l + 1:x_in_r] = True
|
cut[:y_cut, x_in_l + 1:x_in_r] = True
|
||||||
n_cut = int((mask & cut).sum())
|
n_cut = int((mask & cut).sum())
|
||||||
mask &= ~cut
|
mask &= ~cut
|
||||||
self.log(f" 中央区剪除(两镜片开口之间, 开口底缘 y>{self.Y0 + y_cut * self.RES:.1f} 以下): "
|
self.log(f" 中央区剪除(两镜片开口之间, 开口底缘 y>{self.Y0 + y_cut * self.RES:.1f} 以下): "
|
||||||
f"剔除 {n_cut} 格误标鼻面")
|
f"剔除 {n_cut} 格误标鼻面")
|
||||||
else:
|
|
||||||
self.log(" ! 未找到两个镜片开口, 跳过中央区剪除")
|
|
||||||
|
|
||||||
# 膨胀 3 格
|
# ===== 新增: 创建 lens_mask =====
|
||||||
mask = self.dilate_8(mask, 3)
|
self.lens_mask = np.zeros(mask.shape, bool)
|
||||||
|
for pts in big_holes[:2]:
|
||||||
|
for p in pts:
|
||||||
|
self.lens_mask[p[0], p[1]] = True
|
||||||
|
for _ in range(2): # 腐蚀 2 格
|
||||||
|
_nm = self.lens_mask.copy()
|
||||||
|
_nm[1:, :] &= self.lens_mask[:-1, :]; _nm[:-1, :] &= self.lens_mask[1:, :]
|
||||||
|
_nm[:, 1:] &= self.lens_mask[:, :-1]; _nm[:, :-1] &= self.lens_mask[:, 1:]
|
||||||
|
self.lens_mask = _nm
|
||||||
|
# ================================
|
||||||
|
else:
|
||||||
|
self.log(" ! 未找到两个镜片开口, 跳过中央区剪除")
|
||||||
|
|
||||||
# 补内部洞 (镜片开口)
|
# 膨胀 3 格
|
||||||
holes = self.flood_outside(mask)
|
mask = self.dilate_8(mask, 3)
|
||||||
self.log(f" 足迹 {mask.sum()} 格, 补内部洞 {holes.sum()} 格 (镜片开口)")
|
|
||||||
mask = mask | holes
|
|
||||||
|
|
||||||
# 腐蚀
|
# 补内部洞 (镜片开口)
|
||||||
eroded = self.erode_4(mask, self.ERODE)
|
holes = self.flood_outside(mask)
|
||||||
|
self.log(f" 足迹 {mask.sum()} 格, 补内部洞 {holes.sum()} 格 (镜片开口)")
|
||||||
|
mask = mask | holes
|
||||||
|
|
||||||
# y 裁剪 + 最大连通块
|
# 腐蚀
|
||||||
H, W = eroded.shape
|
eroded = self.erode_4(mask, self.ERODE)
|
||||||
jj = np.arange(H)
|
|
||||||
eroded = eroded & (jj[:, None] >= int((self.RY0 - self.Y0) / self.RES)) & \
|
|
||||||
(jj[:, None] <= int((self.RY1 - self.Y0) / self.RES))
|
|
||||||
|
|
||||||
R = np.zeros_like(eroded)
|
# y 裁剪 + 最大连通块
|
||||||
seen = np.zeros_like(eroded, bool)
|
H, W = eroded.shape
|
||||||
best = None
|
jj = np.arange(H)
|
||||||
for j0 in range(H):
|
eroded = eroded & (jj[:, None] >= int((self.RY0 - self.Y0) / self.RES)) & \
|
||||||
for i0 in range(W):
|
(jj[:, None] <= int((self.RY1 - self.Y0) / self.RES))
|
||||||
if eroded[j0, i0] and not seen[j0, i0]:
|
|
||||||
comp = []
|
|
||||||
dq = deque([(j0, i0)])
|
|
||||||
seen[j0, i0] = True
|
|
||||||
touch = False
|
|
||||||
while dq:
|
|
||||||
j, i = dq.popleft()
|
|
||||||
comp.append((j, i))
|
|
||||||
if i in (0, W - 1) or j in (0, H - 1):
|
|
||||||
touch = True
|
|
||||||
for dj, di in ((1,0),(-1,0),(0,1),(0,-1)):
|
|
||||||
nj, ni = j + dj, i + di
|
|
||||||
if 0 <= nj < H and 0 <= ni < W and eroded[nj, ni] and not seen[nj, ni]:
|
|
||||||
seen[nj, ni] = True
|
|
||||||
dq.append((nj, ni))
|
|
||||||
if best is None or len(comp) > len(best[0]):
|
|
||||||
best = (comp, touch)
|
|
||||||
|
|
||||||
if best is None:
|
R = np.zeros_like(eroded)
|
||||||
raise SystemExit("R 为空: 足迹检测失败")
|
seen = np.zeros_like(eroded, bool)
|
||||||
comp, touch = best
|
best = None
|
||||||
for j, i in comp:
|
for j0 in range(H):
|
||||||
R[j, i] = True
|
for i0 in range(W):
|
||||||
self.log(f" R: {R.sum()} 格 ({R.sum()*self.RES*self.RES:.2f} 面积单位), "
|
if eroded[j0, i0] and not seen[j0, i0]:
|
||||||
f"触碰窗口边界={touch} (触碰=侧墙切在头内部, 正常)")
|
comp = []
|
||||||
if R.sum() < 200:
|
dq = deque([(j0, i0)])
|
||||||
raise SystemExit("R 过小")
|
seen[j0, i0] = True
|
||||||
|
touch = False
|
||||||
|
while dq:
|
||||||
|
j, i = dq.popleft()
|
||||||
|
comp.append((j, i))
|
||||||
|
if i in (0, W - 1) or j in (0, H - 1):
|
||||||
|
touch = True
|
||||||
|
for dj, di in ((1,0),(-1,0),(0,1),(0,-1)):
|
||||||
|
nj, ni = j + dj, i + di
|
||||||
|
if 0 <= nj < H and 0 <= ni < W and eroded[nj, ni] and not seen[nj, ni]:
|
||||||
|
seen[nj, ni] = True
|
||||||
|
dq.append((nj, ni))
|
||||||
|
if best is None or len(comp) > len(best[0]):
|
||||||
|
best = (comp, touch)
|
||||||
|
|
||||||
return mask, R
|
if best is None:
|
||||||
|
raise SystemExit("R 为空: 足迹检测失败")
|
||||||
|
comp, touch = best
|
||||||
|
for j, i in comp:
|
||||||
|
R[j, i] = True
|
||||||
|
self.log(f" R: {R.sum()} 格 ({R.sum()*self.RES*self.RES:.2f} 面积单位), "
|
||||||
|
f"触碰窗口边界={touch} (触碰=侧墙切在头内部, 正常)")
|
||||||
|
|
||||||
|
# ===== [抽取自原版] R 裁剪到眼镜真实轮廓 =====
|
||||||
|
_sil = g1f.copy()
|
||||||
|
for _ in range(6):
|
||||||
|
_sm = _sil.copy()
|
||||||
|
_sm[1:, :] |= _sil[:-1, :]; _sm[:-1, :] |= _sil[1:, :]
|
||||||
|
_sm[:, 1:] |= _sil[:, :-1]; _sm[:, :-1] |= _sil[:, 1:]
|
||||||
|
_sm[1:, 1:] |= _sil[:-1, :-1]; _sm[:-1, :-1] |= _sil[1:, 1:]
|
||||||
|
_sm[1:, :-1] |= _sil[:-1, 1:]; _sm[:-1, 1:] |= _sil[1:, :-1]
|
||||||
|
_sil = _sm
|
||||||
|
_sil = _sil | self.flood_outside(_sil)
|
||||||
|
for _ in range(6):
|
||||||
|
_sm = _sil.copy()
|
||||||
|
_sm[1:, :] &= _sil[:-1, :]; _sm[:-1, :] &= _sil[1:, :]
|
||||||
|
_sm[:, 1:] &= _sil[:, :-1]; _sm[:, :-1] &= _sil[:, 1:]
|
||||||
|
_sm[1:, 1:] &= _sil[:-1, :-1]; _sm[:-1, :-1] &= _sil[1:, 1:]
|
||||||
|
_sm[1:, :-1] &= _sil[:-1, 1:]; _sm[:-1, 1:] &= _sil[1:, :-1]
|
||||||
|
_sil = _sm
|
||||||
|
if self.lens_mask is not None:
|
||||||
|
_lexr = self.lens_mask.copy()
|
||||||
|
for _ in range(2):
|
||||||
|
_sm = _lexr.copy()
|
||||||
|
_sm[1:, :] |= _lexr[:-1, :]; _sm[:-1, :] |= _lexr[1:, :]
|
||||||
|
_sm[:, 1:] |= _lexr[:, :-1]; _sm[:, :-1] |= _lexr[:, 1:]
|
||||||
|
_sm[1:, 1:] |= _lexr[:-1, :-1]; _sm[:-1, :-1] |= _lexr[1:, 1:]
|
||||||
|
_sm[1:, :-1] |= _lexr[:-1, 1:]; _sm[:-1, 1:] |= _lexr[1:, :-1]
|
||||||
|
_lexr = _sm
|
||||||
|
_sil = _sil | _lexr
|
||||||
|
_nclip = int((R & ~_sil).sum())
|
||||||
|
R &= _sil
|
||||||
|
self.log(f" R 裁剪到眼镜轮廓: 剔除轮廓外 {_nclip} 格, R={R.sum()} 格")
|
||||||
|
# ===========================================
|
||||||
|
|
||||||
|
if R.sum() < 200:
|
||||||
|
raise SystemExit("R 过小")
|
||||||
|
|
||||||
|
return mask, R
|
||||||
|
|
||||||
# ======================================================================
|
# ======================================================================
|
||||||
# 步骤 4: 构造填充前/后高度场
|
# 步骤 4: 构造填充前/后高度场
|
||||||
@@ -644,6 +688,7 @@ class FillMeshGenerator:
|
|||||||
hb = np.minimum(hb, hf - self.GAP_MIN)
|
hb = np.minimum(hb, hf - self.GAP_MIN)
|
||||||
hb = self.smooth_valid(hb, R, sigma=2.0, it=2)
|
hb = self.smooth_valid(hb, R, sigma=2.0, it=2)
|
||||||
|
|
||||||
|
|
||||||
# 外扩填充 NaN
|
# 外扩填充 NaN
|
||||||
for _ in range(60):
|
for _ in range(60):
|
||||||
nanm = ~np.isfinite(hf)
|
nanm = ~np.isfinite(hf)
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user