Add Variety Boost option for NovelAI image generation. (#4417) Co-authored-by: sirius422 <sirius42@gfw.moe>

4d9f229383ad0b3cd85385d63ac92ba64b2e954e

sirius422 <38351200+sirius422@users.noreply.github.com>

Signed
3 files changed, +36 -0Ignore whitespace
public/scripts/extensions/stable-diffusion/index.js+9 -0
@@ -306,6 +306,7 @@ const defaultSettings = {
306 novel_sm: false,306 novel_sm: false,
307 novel_sm_dyn: false,307 novel_sm_dyn: false,
308 novel_decrisper: false,308 novel_decrisper: false,
309 novel_variety_boost: false,
309310
310 // OpenAI settings311 // OpenAI settings
311 openai_style: 'vivid',312 openai_style: 'vivid',
@@ -482,6 +483,7 @@ async function loadSettings() {
482 $('#sd_novel_sm_dyn').prop('checked', extension_settings.sd.novel_sm_dyn);483 $('#sd_novel_sm_dyn').prop('checked', extension_settings.sd.novel_sm_dyn);
483 $('#sd_novel_sm_dyn').prop('disabled', !extension_settings.sd.novel_sm);484 $('#sd_novel_sm_dyn').prop('disabled', !extension_settings.sd.novel_sm);
484 $('#sd_novel_decrisper').prop('checked', extension_settings.sd.novel_decrisper);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 $('#sd_pollinations_enhance').prop('checked', extension_settings.sd.pollinations_enhance);487 $('#sd_pollinations_enhance').prop('checked', extension_settings.sd.pollinations_enhance);
486 $('#sd_horde').prop('checked', extension_settings.sd.horde);488 $('#sd_horde').prop('checked', extension_settings.sd.horde);
487 $('#sd_horde_nsfw').prop('checked', extension_settings.sd.horde_nsfw);489 $('#sd_horde_nsfw').prop('checked', extension_settings.sd.horde_nsfw);
@@ -1055,6 +1057,11 @@ function onNovelDecrisperInput() {
1055 saveSettingsDebounced();1057 saveSettingsDebounced();
1056}1058}
10571059
1060function onNovelVarietyBoostInput() {
1061 extension_settings.sd.novel_variety_boost = !!$('#sd_novel_variety_boost').prop('checked');
1062 saveSettingsDebounced();
1063}
1064
1058function onPollinationsEnhanceInput() {1065function onPollinationsEnhanceInput() {
1059 extension_settings.sd.pollinations_enhance = !!$('#sd_pollinations_enhance').prop('checked');1066 extension_settings.sd.pollinations_enhance = !!$('#sd_pollinations_enhance').prop('checked');
1060 saveSettingsDebounced();1067 saveSettingsDebounced();
@@ -3234,6 +3241,7 @@ async function generateNovelImage(prompt, negativePrompt, signal) {
3234 negative_prompt: negativePrompt,3241 negative_prompt: negativePrompt,
3235 upscale_ratio: extension_settings.sd.hr_scale,3242 upscale_ratio: extension_settings.sd.hr_scale,
3236 decrisper: extension_settings.sd.novel_decrisper,3243 decrisper: extension_settings.sd.novel_decrisper,
3244 variety_boost: extension_settings.sd.novel_variety_boost,
3237 sm: sm,3245 sm: sm,
3238 sm_dyn: sm_dyn,3246 sm_dyn: sm_dyn,
3239 seed: extension_settings.sd.seed >= 0 ? extension_settings.sd.seed : undefined,3247 seed: extension_settings.sd.seed >= 0 ? extension_settings.sd.seed : undefined,
@@ -4654,6 +4662,7 @@ jQuery(async () => {
4654 $('#sd_novel_sm').on('input', onNovelSmInput);4662 $('#sd_novel_sm').on('input', onNovelSmInput);
4655 $('#sd_novel_sm_dyn').on('input', onNovelSmDynInput);4663 $('#sd_novel_sm_dyn').on('input', onNovelSmDynInput);
4656 $('#sd_novel_decrisper').on('input', onNovelDecrisperInput);4664 $('#sd_novel_decrisper').on('input', onNovelDecrisperInput);
4665 $('#sd_novel_variety_boost').on('input', onNovelVarietyBoostInput);
4657 $('#sd_pollinations_enhance').on('input', onPollinationsEnhanceInput);4666 $('#sd_pollinations_enhance').on('input', onPollinationsEnhanceInput);
4658 $('#sd_comfy_validate').on('click', validateComfyUrl);4667 $('#sd_comfy_validate').on('click', validateComfyUrl);
4659 $('#sd_comfy_url').on('input', onComfyUrlInput);4668 $('#sd_comfy_url').on('input', onComfyUrlInput);
public/scripts/extensions/stable-diffusion/settings.html+4 -0
@@ -451,6 +451,10 @@
451 <input id="sd_novel_decrisper" type="checkbox" />451 <input id="sd_novel_decrisper" type="checkbox" />
452 <small data-i18n="Decrisper">Decrisper</small>452 <small data-i18n="Decrisper">Decrisper</small>
453 </label>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 </div>458 </div>
455459
456 <div data-sd-source="novel,togetherai,pollinations,comfy,drawthings,vlad,auto,horde,extras,stability,bfl" class="marginTop5">460 <div data-sd-source="novel,togetherai,pollinations,comfy,drawthings,vlad,auto,horde,extras,stability,bfl" class="marginTop5">
src/endpoints/novelai.js+23 -0
@@ -11,6 +11,11 @@ const API_NOVELAI = 'https://api.novelai.net';
11const TEXT_NOVELAI = 'https://text.novelai.net';11const TEXT_NOVELAI = 'https://text.novelai.net';
12const IMAGE_NOVELAI = 'https://image.novelai.net';12const IMAGE_NOVELAI = 'https://image.novelai.net';
1313
14// Constants for skip_cfg_above_sigma (Variety+) calculation
15const REFERENCE_PIXEL_COUNT = 1011712; // 832 * 1216 reference image size
16const SIGMA_MAGIC_NUMBER = 19; // Base sigma multiplier for V3 and V4 models
17const SIGMA_MAGIC_NUMBER_V4_5 = 58; // Base sigma multiplier for V4.5 models
18
14// Ban bracket generation, plus defaults19// Ban bracket generation, plus defaults
15const badWordsList = [20const badWordsList = [
16 [3], [49356], [1431], [31715], [34387], [20765], [30702], [10691], [49333], [1266],21 [3], [49356], [1431], [31715], [34387], [20765], [30702], [10691], [49333], [1266],
@@ -112,6 +117,17 @@ function getRepPenaltyWhitelist(model) {
112 return null;117 return null;
113}118}
114119
120function 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
115export const router = express.Router();131export const router = express.Router();
116132
117router.post('/status', async function (req, res) {133router.post('/status', async function (req, res) {
@@ -332,6 +348,13 @@ router.post('/generate-image', async (request, response) => {
332 sm: request.body.sm ?? false,348 sm: request.body.sm ?? false,
333 sm_dyn: request.body.sm_dyn ?? false,349 sm_dyn: request.body.sm_dyn ?? false,
334 uncond_scale: 1,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 use_coords: false,358 use_coords: false,
336 characterPrompts: [],359 characterPrompts: [],
337 reference_image_multiple: [],360 reference_image_multiple: [],