Add Variety Boost option for NovelAI image generation. (#4417) Co-authored-by: sirius422 <sirius42@gfw.moe>
Signed| @@ -306,6 +306,7 @@ const defaultSettings = { | ||
| 306 | 306 | novel_sm: false, |
| 307 | 307 | novel_sm_dyn: false, |
| 308 | 308 | novel_decrisper: false, |
| 309 | + novel_variety_boost: false, | |
| 309 | 310 | |
| 310 | 311 | // OpenAI settings |
| 311 | 312 | openai_style: 'vivid', |
| @@ -482,6 +483,7 @@ async function loadSettings() { | ||
| 482 | 483 | $('#sd_novel_sm_dyn').prop('checked', extension_settings.sd.novel_sm_dyn); |
| 483 | 484 | $('#sd_novel_sm_dyn').prop('disabled', !extension_settings.sd.novel_sm); |
| 484 | 485 | $('#sd_novel_decrisper').prop('checked', extension_settings.sd.novel_decrisper); |
| 486 | + $('#sd_novel_variety_boost').prop('checked', extension_settings.sd.novel_variety_boost); | |
| 485 | 487 | $('#sd_pollinations_enhance').prop('checked', extension_settings.sd.pollinations_enhance); |
| 486 | 488 | $('#sd_horde').prop('checked', extension_settings.sd.horde); |
| 487 | 489 | $('#sd_horde_nsfw').prop('checked', extension_settings.sd.horde_nsfw); |
| @@ -1055,6 +1057,11 @@ function onNovelDecrisperInput() { | ||
| 1055 | 1057 | saveSettingsDebounced(); |
| 1056 | 1058 | } |
| 1057 | 1059 | |
| 1060 | +function onNovelVarietyBoostInput() { | |
| 1061 | + extension_settings.sd.novel_variety_boost = !!$('#sd_novel_variety_boost').prop('checked'); | |
| 1062 | + saveSettingsDebounced(); | |
| 1063 | +} | |
| 1064 | + | |
| 1058 | 1065 | function onPollinationsEnhanceInput() { |
| 1059 | 1066 | extension_settings.sd.pollinations_enhance = !!$('#sd_pollinations_enhance').prop('checked'); |
| 1060 | 1067 | saveSettingsDebounced(); |
| @@ -3234,6 +3241,7 @@ async function generateNovelImage(prompt, negativePrompt, signal) { | ||
| 3234 | 3241 | negative_prompt: negativePrompt, |
| 3235 | 3242 | upscale_ratio: extension_settings.sd.hr_scale, |
| 3236 | 3243 | decrisper: extension_settings.sd.novel_decrisper, |
| 3244 | + variety_boost: extension_settings.sd.novel_variety_boost, | |
| 3237 | 3245 | sm: sm, |
| 3238 | 3246 | sm_dyn: sm_dyn, |
| 3239 | 3247 | seed: extension_settings.sd.seed >= 0 ? extension_settings.sd.seed : undefined, |
| @@ -4654,6 +4662,7 @@ jQuery(async () => { | ||
| 4654 | 4662 | $('#sd_novel_sm').on('input', onNovelSmInput); |
| 4655 | 4663 | $('#sd_novel_sm_dyn').on('input', onNovelSmDynInput); |
| 4656 | 4664 | $('#sd_novel_decrisper').on('input', onNovelDecrisperInput); |
| 4665 | + $('#sd_novel_variety_boost').on('input', onNovelVarietyBoostInput); | |
| 4657 | 4666 | $('#sd_pollinations_enhance').on('input', onPollinationsEnhanceInput); |
| 4658 | 4667 | $('#sd_comfy_validate').on('click', validateComfyUrl); |
| 4659 | 4668 | $('#sd_comfy_url').on('input', onComfyUrlInput); |
| @@ -451,6 +451,10 @@ | ||
| 451 | 451 | <input id="sd_novel_decrisper" type="checkbox" /> |
| 452 | 452 | <small data-i18n="Decrisper">Decrisper</small> |
| 453 | 453 | </label> |
| 454 | + <label class="flex1 checkbox_label" data-i18n="[title]Enable guidance only after body has been formed, to improve diversity and saturation of samples. May reduce relevance" title="Enable guidance only after body has been formed, to improve diversity and saturation of samples. May reduce relevance"> | |
| 455 | + <input id="sd_novel_variety_boost" type="checkbox" /> | |
| 456 | + <small data-i18n="Variety+">Variety+</small> | |
| 457 | + </label> | |
| 454 | 458 | </div> |
| 455 | 459 | |
| 456 | 460 | <div data-sd-source="novel,togetherai,pollinations,comfy,drawthings,vlad,auto,horde,extras,stability,bfl" class="marginTop5"> |
| @@ -11,6 +11,11 @@ const API_NOVELAI = 'https://api.novelai.net'; | ||
| 11 | 11 | const TEXT_NOVELAI = 'https://text.novelai.net'; |
| 12 | 12 | const IMAGE_NOVELAI = 'https://image.novelai.net'; |
| 13 | 13 | |
| 14 | +// Constants for skip_cfg_above_sigma (Variety+) calculation | |
| 15 | +const REFERENCE_PIXEL_COUNT = 1011712; // 832 * 1216 reference image size | |
| 16 | +const SIGMA_MAGIC_NUMBER = 19; // Base sigma multiplier for V3 and V4 models | |
| 17 | +const SIGMA_MAGIC_NUMBER_V4_5 = 58; // Base sigma multiplier for V4.5 models | |
| 18 | + | |
| 14 | 19 | // Ban bracket generation, plus defaults |
| 15 | 20 | const badWordsList = [ |
| 16 | 21 | [3], [49356], [1431], [31715], [34387], [20765], [30702], [10691], [49333], [1266], |
| @@ -112,6 +117,17 @@ function getRepPenaltyWhitelist(model) { | ||
| 112 | 117 | return null; |
| 113 | 118 | } |
| 114 | 119 | |
| 120 | +function calculateSkipCfgAboveSigma(width, height, modelName) { | |
| 121 | + const magicConstant = modelName?.includes('nai-diffusion-4-5') | |
| 122 | + ? SIGMA_MAGIC_NUMBER_V4_5 | |
| 123 | + : SIGMA_MAGIC_NUMBER; | |
| 124 | + | |
| 125 | + const pixelCount = width * height; | |
| 126 | + const ratio = pixelCount / REFERENCE_PIXEL_COUNT; | |
| 127 | + | |
| 128 | + return Math.pow(ratio, 0.5) * magicConstant; | |
| 129 | +} | |
| 130 | + | |
| 115 | 131 | export const router = express.Router(); |
| 116 | 132 | |
| 117 | 133 | router.post('/status', async function (req, res) { |
| @@ -332,6 +348,13 @@ router.post('/generate-image', async (request, response) => { | ||
| 332 | 348 | sm: request.body.sm ?? false, |
| 333 | 349 | sm_dyn: request.body.sm_dyn ?? false, |
| 334 | 350 | uncond_scale: 1, |
| 351 | + skip_cfg_above_sigma: request.body.variety_boost | |
| 352 | + ? calculateSkipCfgAboveSigma( | |
| 353 | + request.body.width ?? 512, | |
| 354 | + request.body.height ?? 512, | |
| 355 | + request.body.model ?? 'nai-diffusion', | |
| 356 | + ) | |
| 357 | + : null, | |
| 335 | 358 | use_coords: false, |
| 336 | 359 | characterPrompts: [], |
| 337 | 360 | reference_image_multiple: [], |