save code

This commit is contained in:
xsl
2026-07-15 01:16:32 +08:00
parent 9386f84c88
commit 4001df2c34
11 changed files with 985 additions and 99 deletions
+4 -2
View File
@@ -236,7 +236,9 @@ console.log(features['四季色彩季型']); // "冷夏型"(中文字段也保
<table>
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
<tr><td><code>hairline_images[]</code></td><td>object[]</td><td>选中发型列表,每项含 <code>hairline_type</code><code>image_middle_url</code>/<code>image_high_url</code>/<code>image_low_url</code> 三档<strong>透明 PNG 叠图</strong>(仅曲线,需叠加原图)、<code>grown_image_url</code> 生发图(完整人像,失败为 null)、<code>order</code></td></tr>
<tr><td><code>best_hairline_center_point</code></td><td>object</td><td>首个选中发型 middle 档发际线中心点像素坐标 <code>{ x: number, y: number }</code></td></tr>
<tr><td><code>best_hairline_center_point</code></td><td>object \| null</td><td>首个选中发型 <strong>middle 档</strong>发际线中心点像素坐标 <code>{ x: number, y: number }</code></td></tr>
<tr><td><code>high_hairline_center_point</code></td><td>object \| null</td><td>同上,<strong>high 档</strong>发际线中点(发际线偏高,y 更小)</td></tr>
<tr><td><code>low_hairline_center_point</code></td><td>object \| null</td><td>同上,<strong>low 档</strong>发际线中点(发际线偏低,y 更大)</td></tr>
<tr><td><code>face_measure</code></td><td>object \| null</td><td>复用<a href="#if1">接口1</a><strong>四庭七眼测量数值</strong>(不含标注图)。独立流程,测量失败时为 <code>null</code>,不影响发际线主结果。结构见下表</td></tr>
</table>
@@ -278,7 +280,7 @@ console.log(features['四季色彩季型']); // "冷夏型"(中文字段也保
<tr><td><code>annotated_image_url</code></td><td>string</td><td>标注 PNG URL(仅标注图层,透明底)</td></tr>
<tr><td><code>face_total_height_cm</code></td><td>number</td><td>面部总高度(cm= 三庭之和</td></tr>
<tr><td><code>four_courts</code></td><td>object</td><td>三庭:upper/middle/lower,各含 _cm 和 ratios<strong>无 top_court</strong></td></tr>
<tr><td><code>seven_eyes</code></td><td>object</td><td>七眼:eye_width/face_width/inter_eye_distance</td></tr>
<tr><td><code>seven_eyes</code></td><td>object</td><td>七眼:<code>eye_width_cm</code>/<code>face_width_cm</code>/<code>inter_eye_distance_cm</code> + <code>ratios</code> + <strong><code>eye2</code>~<code>eye6</code></strong>(左脸颊/左眼/两眼间距/右眼/右脸颊,5 段宽度 cm;<strong>无 eye1/eye7</strong></td></tr>
<tr><td><code>landmarks</code></td><td>object</td><td>4 个关键点:hairline/brow_center/nose_bottom/chin_tip<strong>无 hair_top</strong></td></tr>
</table>
</div>
+100 -13
View File
@@ -55,7 +55,7 @@
<h1>接口11 调试页 <span style="font-size:13px;color:#888">(带前后端日志)</span></h1>
<div class="subtitle">
独立调试页,每一步都记录日志。提交后展开"前端日志"和"后端日志",点按钮可下载。<br>
重点排查:遮罩是否走 pushed、①-f/①-g 是否有图、最终遮罩是否正确。遮罩固定 pushed融合固定 multiband。
重点排查:遮罩是否走 pushed、①-f/①-g 是否有图、最终遮罩是否正确。遮罩固定 pushed融合默认 multiband(可调,含两段式)
</div>
<div class="card">
@@ -85,10 +85,6 @@
<option value="bisenet">bisenet</option>
</select>
</div>
<div class="pf">
<label>erode_cm <span class="desc">baseline参考内缩(cm)</span></label>
<div class="row"><input type="number" id="erodeCm" min="0" max="5" step="0.1" value="0.6"></div>
</div>
<div class="pf">
<label>hairline_push_cm <span class="desc">发际线外推(cm)</span></label>
<div class="row"><input type="number" id="hairlinePushCm" min="0" max="3" step="0.1" value="1.0"></div>
@@ -100,6 +96,10 @@
<option value="contour">contour(形态学轮廓)</option>
</select>
</div>
<div class="pf">
<label>is_hr <span class="desc">高清模式(换发型输出1152×1536)</span></label>
<div class="row"><input type="checkbox" id="isHr" style="width:18px;height:18px"><span class="desc">仅 swaphair 后端;关=576×768</span></div>
</div>
<div class="pf">
<label>mb_levels <span class="desc">多频段金字塔层数</span></label>
<div class="row"><input type="number" id="mbLevels" min="2" max="6" step="1" value="5"></div>
@@ -108,8 +108,59 @@
<label>edge_erode_px <span class="desc">贴图前遮罩内缩(px)</span></label>
<div class="row"><input type="number" id="edgeErodePx" min="0" max="40" step="1" value="3"></div>
</div>
<div class="pf">
<label>blend_method <span class="desc">接缝融合方法</span></label>
<select id="blendMethod">
<option value="multiband">multiband(多频段金字塔,默认)</option>
<option value="two_stage">two_stage(泊松→多频段,大色差)</option>
<option value="seamless">seamless(泊松无缝克隆)</option>
<option value="feather">feather(高斯羽化)</option>
<option value="alpha_gradient">alpha_gradient(距离变换)</option>
</select>
</div>
<div class="pf">
<label>color_match <span class="desc">融合前颜色迁移(消除色差)</span></label>
<div class="row"><input type="checkbox" id="colorMatch" checked style="width:18px;height:18px"><span class="desc" id="cmNote">multiband/feather 生效;seamless/two_stage 自带调色</span></div>
</div>
<div class="pf">
<label>color_match_strength <span class="desc">颜色迁移强度(0~1)</span></label>
<div class="row"><input type="number" id="cmStrength" min="0" max="1" step="0.05" value="1.0"></div>
</div>
<div class="pf">
<label>mb_feather_px <span class="desc">最细层掩码羽化(px)</span></label>
<div class="row"><input type="number" id="mbFeatherPx" min="0" max="5" step="1" value="1"></div>
</div>
<div class="pf">
<label>transition_band_px <span class="desc">过渡带边距(-1=自动)</span></label>
<div class="row"><input type="number" id="transitionBandPx" min="-1" max="128" step="1" value="-1"></div>
</div>
<div class="pf">
<label>redraw <span class="desc">发际线带重绘开关</span></label>
<div class="row"><input type="checkbox" id="redraw" style="width:18px;height:18px"><span class="desc">开:发际线带(外推↔内推)重绘,final输入,swapHair/Flux-2两路对比</span></div>
</div>
<div class="pf">
<label>inpainting_fill <span class="desc">重绘填充(治染绿)</span></label>
<select id="inpaintingFill">
<option value="1">1=填充噪声(默认/原始)</option>
<option value="0">0=保留原图(治染绿)</option>
<option value="2">2=纯色填充</option>
<option value="3">3=潜变量噪声</option>
</select>
</div>
<div class="pf">
<label>mask_blur <span class="desc">重绘遮罩边缘模糊(px)</span></label>
<div class="row"><input type="number" id="maskBlur" min="0" max="64" step="1" value="11"></div>
</div>
<div class="pf">
<label>mask_dilate_scale <span class="desc">重绘遮罩膨胀缩放</span></label>
<div class="row"><input type="number" id="maskDilateScale" min="0" max="4" step="0.1" value="1.0"></div>
</div>
</div>
<div style="font-size:12px;color:#888;margin-top:8px">遮罩算法固定 pushed、融合固定 multiband,无需选择。</div>
<div class="pf" style="margin-top:14px">
<label>comfyui_prompt <span class="desc">redraw C路(Flux-2)提示词,留空=默认「补充遮罩区域的头发,加一点美颜」</span></label>
<textarea id="comfyuiPrompt" rows="2" style="width:100%;padding:8px;border:1px solid #ddd;border-radius:8px;font-size:13px;font-family:inherit;resize:vertical" placeholder="留空用默认提示词"></textarea>
</div>
<div style="font-size:12px;color:#888;margin-top:8px">遮罩算法固定 pushed。融合默认 multiband,可在上方 blend_method 切换对比。勾选 redraw 开启发际线带重绘(swapHair/Flux-2 两路对比)。</div>
</div>
<div class="status hidden" id="statusBar"></div>
@@ -176,6 +227,9 @@ const STEPS = [
{ key: 'hard_paste', title: '③ 严格贴回', sub: '遮罩内=生成,外=原图' },
{ key: 'alpha', title: '④ 融合权重', sub: '白=用生成图' },
{ key: 'final', title: '④ 接缝融合(最终)', sub: '最终输出' },
{ key: 'redraw_band_overlay', title: '⑤-① 发际线重绘带', sub: '紫=外推↔内推之间的带(以内轮廓为中心,两侧各push_cm,仅redraw' },
{ key: 'redraw_a', title: '⑤-② 带重绘 swapHair路', sub: 'final+带重绘(swapHair)→final融合(仅redraw' },
{ key: 'redraw_c', title: '⑤-③ 带重绘 Flux-2路', sub: 'final+带重绘(Flux-2,保色不染绿)→final融合(仅redraw' },
];
function $(id) { return document.getElementById(id); }
@@ -194,7 +248,7 @@ function stepCard(title, sub, src) {
}
function renderResult(d) {
flog('renderResult 开始(固定 pushed + multiband', 'info');
flog('renderResult 开始 (mask=pushed, blend=' + (d.blend_method || '?') + ')', 'info');
const s = d.steps || {};
$('finalInput').src = pick(s, 'input') || '';
$('finalInput').onclick = function(){ zoom(this.src); };
@@ -202,12 +256,22 @@ function renderResult(d) {
$('finalOut').onclick = function(){ zoom(this.src); };
const grid = $('stepsGrid'); grid.innerHTML = '';
// 固定 pushed:只展示 pushed 相关步骤(不展示 top_fill/closed
// redraw 步骤(redraw_band/redraw_a/redraw_c)只在开启 redraw 且有图时才渲染
const showKeys = new Set(['baseline_overlay','upper_overlay','hair_seg_overlay',
'hairline_overlay','pushed_overlay','mask_overlay','mask','swap_raw','hard_paste','alpha','final']);
$('stepsInfo').textContent = '(固定 pushed + multiband)';
'hairline_overlay','pushed_overlay','mask_overlay','mask','swap_raw','hard_paste','alpha','final',
'redraw_band_overlay','redraw_a','redraw_c']);
const redrawKeys = new Set(['redraw_band_overlay','redraw_a','redraw_c']);
$('stepsInfo').textContent = '(mask=pushed, blend=' + (d.blend_method || '?') + ')';
if (d.redraw && d.redraw.enabled) {
flog('发际线带重绘已开启 band_pixels=' + d.redraw.band_pixels, 'info');
} else if (d.redraw && d.redraw.error) {
flog('发际线带重绘失败: ' + d.redraw.error, 'warn');
}
STEPS.filter(st => showKeys.has(st.key)).forEach(st => {
const src = pick(s, st.key);
const hasImg = src && src.length > 50;
// redraw 步骤无图(未开启或失败)时跳过,不占位
if (redrawKeys.has(st.key) && !hasImg) return;
flog(' 渲染 ' + st.key + ': ' + (hasImg ? '有图(' + src.length + '字符)' : '无图'), hasImg ? 'info' : 'warn');
grid.appendChild(stepCard(st.title, st.sub, src));
});
@@ -223,11 +287,22 @@ async function submitTest() {
flog('前端读取 hairline_edge=' + ($('hairlineEdge').value || '(默认)'), 'info');
flog('前端读取 hairline_id=' + $('hairlineId').value, 'info');
flog('前端读取 seg_model=' + $('segModel').value, 'info');
flog('前端读取 is_hr=' + $('isHr').checked, 'info');
flog('前端读取 mb_levels=' + ($('mbLevels').value || '(默认)'), 'info');
flog('前端读取 blend_method=' + $('blendMethod').value, 'info');
flog('前端读取 color_match=' + $('colorMatch').checked, 'info');
flog('前端读取 color_match_strength=' + ($('cmStrength').value || '(默认)'), 'info');
flog('前端读取 mb_feather_px=' + ($('mbFeatherPx').value || '(默认)'), 'info');
flog('前端读取 transition_band_px=' + ($('transitionBandPx').value || '(默认)'), 'info');
flog('前端读取 redraw=' + $('redraw').checked, 'info');
flog('前端读取 inpainting_fill=' + $('inpaintingFill').value, 'info');
flog('前端读取 mask_blur=' + ($('maskBlur').value || '(默认)'), 'info');
flog('前端读取 mask_dilate_scale=' + ($('maskDilateScale').value || '(默认)'), 'info');
flog('前端读取 comfyui_prompt=' + ($('comfyuiPrompt').value.trim() || '(默认)'), 'info');
const btn = $('submitBtn');
btn.disabled = true; btn.textContent = '⏳ 请求中...';
setStatus('正在请求(固定 pushed + multiband...', 'info');
setStatus('正在请求 (mask=pushed, blend=' + $('blendMethod').value + ')...', 'info');
$('resultsArea').classList.remove('hidden');
const form = new FormData();
@@ -235,15 +310,25 @@ async function submitTest() {
form.append('hairline_id', $('hairlineId').value);
form.append('gen_backend', 'swaphair');
form.append('hairgrow_strength', '0.75');
form.append('is_hr', 'false');
form.append('is_hr', $('isHr').checked ? 'true' : 'false');
form.append('seg_model', $('segModel').value);
form.append('erode_cm', $('erodeCm').value || '0.6');
form.append('swap_mode', 'ext_mask');
form.append('edge_erode_px', $('edgeErodePx').value || '3');
form.append('denoising_strength', '0.6');
form.append('mb_levels', $('mbLevels').value || '5');
form.append('hairline_push_cm', $('hairlinePushCm').value || '1.0');
form.append('hairline_edge', $('hairlineEdge').value);
form.append('blend_method', $('blendMethod').value);
form.append('color_match', $('colorMatch').checked ? 'true' : 'false');
form.append('color_match_strength', $('cmStrength').value || '1.0');
form.append('mb_feather_px', $('mbFeatherPx').value || '1');
form.append('transition_band_px', $('transitionBandPx').value || '-1');
form.append('redraw', $('redraw').checked ? 'true' : 'false');
form.append('inpainting_fill', $('inpaintingFill').value || '1');
form.append('mask_blur', $('maskBlur').value || '11');
form.append('mask_dilate_scale', $('maskDilateScale').value || '1.0');
const cp = $('comfyuiPrompt').value.trim();
if (cp) form.append('comfyui_prompt', cp);
// 记录发出去的 form 字段
const sentFields = {};
@@ -266,6 +351,8 @@ async function submitTest() {
const d = json.data;
flog('后端返回 mask_type=' + d.mask_type + ' blend=' + d.blend_method + ' mask_pixels=' + d.mask_pixels, 'info');
flog('后端返回 hairline_push_cm=' + d.hairline_push_cm + ' hairline_edge=' + d.hairline_edge, 'info');
flog('后端返回 color_match=' + d.color_match + ' cm_strength=' + d.color_match_strength + ' mb_feather_px=' + d.mb_feather_px + ' transition_band_px=' + d.transition_band_px, 'info');
flog('后端返回 inpainting_fill=' + d.inpainting_fill + ' mask_blur=' + d.mask_blur + ' mask_dilate_scale=' + d.mask_dilate_scale, 'info');
flog('后端 _rid=' + d._rid, 'info');
// 详细记录 steps 每个字段长度
const s = d.steps || {};
@@ -316,7 +403,7 @@ async function downloadBackendLog() {
$('imageFile').addEventListener('change', function(){ if(this.files.length) flog('选择图片: ' + this.files[0].name, 'info'); });
flog('调试页加载完成', 'info');
flog('遮罩固定 pushed融合固定 multiband,选好图片直接提交', 'info');
flog('遮罩固定 pushed融合默认 multiband(可调,含两段式 two_stage,选好图片直接提交', 'info');
</script>
</body>
</html>
+10 -3
View File
@@ -132,8 +132,12 @@
<div class="card-header"><span>🎯 所有发型结果(三档 + 生发图)</span><span style="font-weight:400;font-size:12px;color:#9ca3af" id="resultCount">共 0 个发型</span></div>
<div class="card-body">
<div class="coord-box" style="margin-top:0;margin-bottom:16px">
<div class="label">📍 最佳发际线中心点(best_hairline_center_point,首个发型 middle 档)— 原图像素坐标</div>
<div class="value" id="centerPoint"></div>
<div class="label">📍 发际线中心点(首个发型三档)— 原图像素坐标</div>
<div class="value">
middle: <span id="centerPoint"></span>
high: <span id="centerHigh"></span>
low: <span id="centerLow"></span>
</div>
</div>
<div id="resultsGrid"></div>
</div>
@@ -211,7 +215,10 @@ async function submitTest() {
if (json.code === 0) {
_images = json.data.hairline_images || [];
_center = json.data.best_hairline_center_point;
$('centerPoint').textContent = _center ? '(' + _center.x + ', ' + _center.y + ')' : '—';
const _fmtPt = p => p ? '(' + p.x + ', ' + p.y + ')' : '—';
$('centerPoint').textContent = _fmtPt(json.data.best_hairline_center_point);
$('centerHigh').textContent = _fmtPt(json.data.high_hairline_center_point);
$('centerLow').textContent = _fmtPt(json.data.low_hairline_center_point);
$('resultCount').textContent = '共 ' + _images.length + ' 个发型';
setStatus('✅ ' + _images.length + ' 个发型 × 三档 (' + _elapsed + 's)', 'success');
renderGrid();