Skip to content

Commit e88b4ba

Browse files
committed
feat: builder — render Florence2 step sub-panel with 14 fields
Adds the Florence2 Hi-Res Fix option to the upscale mode dropdown and a sub-panel that renders when selected: 4 detection fields, 4 crop/resize fields, 5 mask-shape fields (grow_expand + 4 feather sides), model/lora source dropdown, and the no-detection dropdown. Hires sampling fields (denoise/steps/cfg/sampler /scheduler) are reused from the existing hires_only mode rendering — Florence2 sets showHires=true so they appear naturally. Combos counter treats Florence2 as 1-output-per-input (no cartesian multiplier).
1 parent 67eba95 commit e88b4ba

1 file changed

Lines changed: 124 additions & 1 deletion

File tree

web/conf_builder/conf-builder-config-management.js

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5938,6 +5938,7 @@ export function renderUpscalingSection(node, container, modelLists) {
59385938
<option value="model_only" ${ucfg.mode === "model_only" ? 'selected' : ''}>Model Upscale Only</option>
59395939
<option value="model_then_hires" ${ucfg.mode === "model_then_hires" ? 'selected' : ''}>Model Upscale \u2192 HiRes Fix</option>
59405940
<option value="seedvr2" ${ucfg.mode === "seedvr2" ? 'selected' : ''}>SeedVR2 Upscale</option>
5941+
<option value="florence2_hires" ${ucfg.mode === "florence2_hires" ? 'selected' : ''}>Florence2 Hi-Res Fix</option>
59415942
`;
59425943
modeSelect.onchange = () => {
59435944
ucfg.mode = modeSelect.value;
@@ -5956,9 +5957,10 @@ export function renderUpscalingSection(node, container, modelLists) {
59565957
resizeSelect.onchange = () => { ucfg.resize_method = resizeSelect.value; node.saveState(); };
59575958
grid.appendChild(createInputGroup("Resize Method", resizeSelect));
59585959

5959-
const showHires = ucfg.mode === "hires_only" || ucfg.mode === "model_then_hires";
5960+
const showHires = ucfg.mode === "hires_only" || ucfg.mode === "model_then_hires" || ucfg.mode === "florence2_hires";
59605961
const showModel = ucfg.mode === "model_only" || ucfg.mode === "model_then_hires";
59615962
const showSeedVR2 = ucfg.mode === "seedvr2";
5963+
const showFlorence2 = ucfg.mode === "florence2_hires";
59625964

59635965
// --- HiRes fields (multi-value: ratios, denoise) ---
59645966
if (showHires) {
@@ -6362,6 +6364,126 @@ export function renderUpscalingSection(node, container, modelLists) {
63626364
grid.appendChild(createInputGroup("Cache VAE Model", vaeCacheCb));
63636365
}
63646366

6367+
// --- Florence2 Hi-Res Fix sub-panel ---
6368+
if (showFlorence2) {
6369+
if (!ucfg.florence2) ucfg.florence2 = createDefaultFlorence2Options();
6370+
const f2 = ucfg.florence2;
6371+
6372+
// --- Group A: Florence2 detection ---
6373+
const f2ModelSelect = document.createElement("select");
6374+
f2ModelSelect.className = "cb-select";
6375+
f2ModelSelect.title = "Florence2 model for segmentation. base/large work for referring_expression_segmentation; ft variants slightly worse.";
6376+
[
6377+
"microsoft/Florence-2-base",
6378+
"microsoft/Florence-2-base-ft",
6379+
"microsoft/Florence-2-large",
6380+
"microsoft/Florence-2-large-ft",
6381+
].forEach(name => {
6382+
const opt = document.createElement("option");
6383+
opt.value = name; opt.textContent = name;
6384+
if (f2.model === name) opt.selected = true;
6385+
f2ModelSelect.appendChild(opt);
6386+
});
6387+
f2ModelSelect.onchange = () => { f2.model = f2ModelSelect.value; node.saveState(); };
6388+
grid.appendChild(createInputGroup("Florence2 Model", f2ModelSelect));
6389+
6390+
const f2TextInput = document.createElement("input");
6391+
f2TextInput.type = "text"; f2TextInput.className = "cb-input";
6392+
f2TextInput.value = f2.text_input || "face";
6393+
f2TextInput.placeholder = "face, head, hands, eyes...";
6394+
f2TextInput.title = "What to detect. Try: face, head, hands, eyes, person, clothing.";
6395+
f2TextInput.onchange = () => { f2.text_input = f2TextInput.value; node.saveState(); };
6396+
grid.appendChild(createInputGroup("Detect What?", f2TextInput));
6397+
6398+
const f2MaskSelInput = document.createElement("input");
6399+
f2MaskSelInput.type = "text"; f2MaskSelInput.className = "cb-input";
6400+
f2MaskSelInput.value = f2.output_mask_select || "";
6401+
f2MaskSelInput.placeholder = '(empty = all)';
6402+
f2MaskSelInput.title = "Empty = all detections. '0' = primary/largest. '0,2' = pick specific indices.";
6403+
f2MaskSelInput.onchange = () => { f2.output_mask_select = f2MaskSelInput.value; node.saveState(); };
6404+
grid.appendChild(createInputGroup("Output Mask Select", f2MaskSelInput));
6405+
6406+
// --- Group B: Crop & resize ---
6407+
const f2MpInput = document.createElement("input");
6408+
f2MpInput.type = "number"; f2MpInput.className = "cb-input";
6409+
f2MpInput.value = f2.target_megapixels ?? 1.0;
6410+
f2MpInput.min = 0.25; f2MpInput.max = 4.0; f2MpInput.step = 0.25;
6411+
f2MpInput.title = "Resize cropped region to this many megapixels before the hi-res pass. 1.0 ≈ 1024×1024.";
6412+
f2MpInput.onchange = () => { f2.target_megapixels = parseFloat(f2MpInput.value); node.saveState(); };
6413+
grid.appendChild(createInputGroup("Hi-Res Target (MP)", f2MpInput));
6414+
6415+
const f2PadInput = document.createElement("input");
6416+
f2PadInput.type = "number"; f2PadInput.className = "cb-input";
6417+
f2PadInput.value = f2.crop_padding ?? 64;
6418+
f2PadInput.min = 0; f2PadInput.max = 256; f2PadInput.step = 8;
6419+
f2PadInput.title = "Padding around detected polygon. Higher = more blending context.";
6420+
f2PadInput.onchange = () => { f2.crop_padding = parseInt(f2PadInput.value, 10); node.saveState(); };
6421+
grid.appendChild(createInputGroup("Crop Padding (px)", f2PadInput));
6422+
6423+
const f2MinCropInput = document.createElement("input");
6424+
f2MinCropInput.type = "number"; f2MinCropInput.className = "cb-input";
6425+
f2MinCropInput.value = f2.min_crop_resolution ?? 256;
6426+
f2MinCropInput.min = 64; f2MinCropInput.max = 4096; f2MinCropInput.step = 8;
6427+
f2MinCropInput.title = "Floor for crop size — prevents tiny inpaints.";
6428+
f2MinCropInput.onchange = () => { f2.min_crop_resolution = parseInt(f2MinCropInput.value, 10); node.saveState(); };
6429+
grid.appendChild(createInputGroup("Min Crop Res (px)", f2MinCropInput));
6430+
6431+
const f2MaxCropInput = document.createElement("input");
6432+
f2MaxCropInput.type = "number"; f2MaxCropInput.className = "cb-input";
6433+
f2MaxCropInput.value = f2.max_crop_resolution ?? 1536;
6434+
f2MaxCropInput.min = 64; f2MaxCropInput.max = 4096; f2MaxCropInput.step = 8;
6435+
f2MaxCropInput.title = "Ceiling for crop size — prevents OOM on full-frame detections.";
6436+
f2MaxCropInput.onchange = () => { f2.max_crop_resolution = parseInt(f2MaxCropInput.value, 10); node.saveState(); };
6437+
grid.appendChild(createInputGroup("Max Crop Res (px)", f2MaxCropInput));
6438+
6439+
// --- Group C: Mask shaping ---
6440+
const f2GrowInput = document.createElement("input");
6441+
f2GrowInput.type = "number"; f2GrowInput.className = "cb-input";
6442+
f2GrowInput.value = f2.grow_expand ?? 32;
6443+
f2GrowInput.min = -64; f2GrowInput.max = 256; f2GrowInput.step = 1;
6444+
f2GrowInput.title = "GrowMask expand. Negative shrinks. Adds pixels to polygon edges.";
6445+
f2GrowInput.onchange = () => { f2.grow_expand = parseInt(f2GrowInput.value, 10); node.saveState(); };
6446+
grid.appendChild(createInputGroup("Grow Mask (px)", f2GrowInput));
6447+
6448+
["left", "top", "right", "bottom"].forEach(side => {
6449+
const inp = document.createElement("input");
6450+
inp.type = "number"; inp.className = "cb-input";
6451+
const key = `feather_${side}`;
6452+
inp.value = f2[key] ?? 128;
6453+
inp.min = 0; inp.max = 256; inp.step = 1;
6454+
inp.title = `FeatherMask ${side}-side alpha falloff in pixels.`;
6455+
inp.onchange = () => { f2[key] = parseInt(inp.value, 10); node.saveState(); };
6456+
grid.appendChild(createInputGroup(`Feather ${side.charAt(0).toUpperCase() + side.slice(1)} (px)`, inp));
6457+
});
6458+
6459+
// --- Group E: Model/LoRA source ---
6460+
const f2SourceSelect = document.createElement("select");
6461+
f2SourceSelect.className = "cb-select";
6462+
f2SourceSelect.title = "'From manifest' uses each image's original model/LoRA. 'From this Builder config' uses the same model the upscale session loaded.";
6463+
[
6464+
["from_manifest", "From manifest (per-image)"],
6465+
["from_builder", "From this Builder config"],
6466+
].forEach(([val, lbl]) => {
6467+
const opt = document.createElement("option");
6468+
opt.value = val; opt.textContent = lbl;
6469+
if (f2.model_source === val) opt.selected = true;
6470+
f2SourceSelect.appendChild(opt);
6471+
});
6472+
f2SourceSelect.onchange = () => { f2.model_source = f2SourceSelect.value; node.saveState(); };
6473+
grid.appendChild(createInputGroup("Model/LoRA Source", f2SourceSelect));
6474+
6475+
// --- Group F: No-detection ---
6476+
const f2NoDetSelect = document.createElement("select");
6477+
f2NoDetSelect.className = "cb-select";
6478+
f2NoDetSelect.title = "What to do when Florence2 finds nothing in the image.";
6479+
const optSkip = document.createElement("option");
6480+
optSkip.value = "skip"; optSkip.textContent = "Skip + log";
6481+
if ((f2.on_no_detection || "skip") === "skip") optSkip.selected = true;
6482+
f2NoDetSelect.appendChild(optSkip);
6483+
f2NoDetSelect.onchange = () => { f2.on_no_detection = f2NoDetSelect.value; node.saveState(); };
6484+
grid.appendChild(createInputGroup("If No Detection", f2NoDetSelect));
6485+
}
6486+
63656487
card.appendChild(grid);
63666488

63676489
// Iteration count for this step
@@ -6372,6 +6494,7 @@ export function renderUpscalingSection(node, container, modelLists) {
63726494
const models = Math.max(1, (ucfg.upscale_models || []).length);
63736495
let combos = 1;
63746496
if (showSeedVR2) combos = 1; // SeedVR2 = 1 output per input
6497+
else if (showFlorence2) combos = 1; // Florence2 = 1 output per input (uses hires_denoise as a single value, not array)
63756498
else if (showHires) combos *= ratios * denoises;
63766499
if (showModel) combos *= models;
63776500
const repeatCount = ucfg.repeat || 1;

0 commit comments

Comments
 (0)