/** * OutpaintIt * v.1.8.6, last updated: 3/22/2024 * By Gary W. * * A simple outpatining approach. 5 buttons are added with this one file. * One click to outpaint in one of the 4 directions, or OutpaintAll to outpaint all 4 directions at once. * * Maximum output depends on the resolution set below. Resolution values in the output may or may not not conform * to those available in the UI dropdown. * * Free to use with the CMDR2 Stable Diffusion UI, Easy Diffusion. * */ //needs to be outside of the wrapper, as the input items are in the main UI. var OutpaintItSettings = { useChangedPrompt: false, useChangedModel: false, useChangedSampler: false, useExtendImage: false }; /* EDIT THIS to put in the maximum resolutions your video card can handle. These values work (usually) for the Nvidia 2060 Super with 8GB VRAM. If you go too large, you'll see "Error: CUDA out of memory". */ (function () { "use strict" var outpaintMaxTotalResolution = 10000000; //6000000; //was: 1280 * 1280; //put max 'low' mode resolution here, max possible size when low mode is on var outpaintMaxTurboResolution = 1088 * 1664; //was: 1536 * 896; //put max resolution (other than 'low' mode) here var maxNoVaeTiling = 2200000; //5500000; //max resolution to allow no VAE tiling. Turn off VAE tiling for larger images. const outpaintSizeIncrease = 128; //This can be modified by increments/decrements of 64, as desired //const outpaintMaskOverlap = 36; //Need some overlap on the mask (minimum of 8px) const outpaintPercentToKeep = 1;//.5; //Amount of random pixels to retain, to bias effect const maskFade = 0.07; const maskExtraOverlap = 0; const maskExtraOffset = -24; const blackColor = 'rgba(255,255,255,0)'; //'rgba(0,0,0,0)'; function outpaintSetPixels(imageData) { function guassianRand() { //approximation of Gaussian, from Stackoverflow var rand =0; for (var i=0; i<6; i+=1) { rand += Math.random(); } return rand/6; } function randomPixel() { //return Math.floor(Math.random() * 256); //0 to 255 return Math.floor(guassianRand() * 256); //0 to 255 //Math.random() doesn't seem too random. //return Math.floor(1-Math.pow(Math.random(),2) * 256); } // get the pixel data array var pixels = imageData.data; var numPixels = pixels.length/4; // loop through each pixel for (var i = 0; i < pixels.length; i += 4) { //if not blank/black and keeping the original pixel, continue. (If black, probably have not pre-filled the space.) if (!(pixels[i]==0 && pixels[i+1]==0 && pixels[i+2]==0) && OutpaintItSettings.useExtendImage && Math.random()=200) { //50*4 // newPixel=(Math.floor(Math.random()*100)-50)*4+i; // } // pixels[i] = pixels[newPixel]; // pixels[i + 1] = pixels[newPixel+1]; // pixels[i + 2] = pixels[newPixel+2]; // pixels[i + 3] = 255; // } //always give some color bias to noise let newPixel=Math.floor(Math.random()*numPixels)*4; if(Math.random()<.2 && i=200) { //50*4 newPixel=(Math.floor(Math.random()*100)-50)*4+i; //note that if filled with more noise, this is going to reach back and pick up earlier noise values - not ideal pixels[i] = pixels[newPixel]; pixels[i + 1] = pixels[newPixel+1]; pixels[i + 2] = pixels[newPixel+2]; pixels[i + 3] = 255; } else if (!OutpaintItSettings.useExtendImage) { // get the red, green, blue (but not alpha) values var r;// = pixels[i]; var g;// = pixels[i + 1]; var b;// = pixels[i + 2]; //var a = pixels[i + 3]; // randomize the pixel values r = randomPixel(); g = randomPixel(); b = randomPixel(); //a = 255; // set the new pixel values back to the array pixels[i] = r; pixels[i + 1] = g; pixels[i + 2] = b; pixels[i + 3] = 255; } } } var contrastAmount=2; //Contrast from: //https://stackoverflow.com/questions/10521978/html5-canvas-image-contrast //https://jsfiddle.net/88k7zj3k/6/ function contrastImage(imageData, contrast) { // contrast as an integer percent var data = imageData.data; // original array modified, but canvas not updated contrast *= 2.55; // or *= 255 / 100; scale integer percent to full range var factor = (255 + contrast) / (255.01 - contrast); //add .1 to avoid /0 error for(var i=0;i=0) { result = true; } return result; } function desiredModelName(origRequest) { //Grab the model name from the user-input area instead of the original image. if (OutpaintItSettings.useChangedModel) { return $("#editor-settings #stable_diffusion_model")[0].dataset.path; } else { return origRequest.use_stable_diffusion_model; //for the original model } } function desiredVaeName(origRequest) { //Grab the name from the user-input area instead of the original image. if (OutpaintItSettings.useChangedModel) { return $("#editor-settings #vae_model")[0].dataset.path; } else { return origRequest.use_vae_model; //for the original model } } //allow changing the sampler, for those who swap to a different sampler for img2img on intermediate steps function desiredSamplerName(origRequest) { //Grab the name from the user-input area instead of the original image. if (OutpaintItSettings.useChangedSampler) { return $("#editor-settings #sampler_name")[0].value } else { return origRequest.sampler_name; //for the original model } } function calcOutpaintSizeIncrease(image) { //For each 2mp, add another block of 64 to the outpaint size. For larger images, the default value is a bit thin. return outpaintSizeIncrease + 64 * Math.floor((image.naturalWidth*image.naturalHeight)/2000000) // //If the image is greater than 3.5mp, increase the size of the outpaint. For larger images, the default value is a bit thin. // if(image.naturalWidth*image.naturalHeight > 3500000) { // return outpaintSizeIncrease * 2; // } // else { // return outpaintSizeIncrease; // } } function outpaintGetTaskRequest(origRequest, image, widen, all=false) { let newTaskRequest = getCurrentUserRequest(); let initialPromptStrength = (OutpaintItSettings.useExtendImage ? 0.89 : 0.95); var desiredModel=desiredModelName(origRequest); var isTurbo=isModelTurbo(desiredModel); newTaskRequest.reqBody = Object.assign({}, origRequest, { init_image: image.src, prompt_strength: initialPromptStrength+Math.random()*(1-initialPromptStrength) - (OutpaintItSettings.useExtendImage ? 0.09 : 0.0), //be sure the values add up to 1 or less width: image.naturalWidth + ((widen || all)?calcOutpaintSizeIncrease(image):0), height: image.naturalHeight + ((!widen || all)?calcOutpaintSizeIncrease(image):0), //guidance_scale: Math.max(origRequest.guidance_scale,15), //Some suggest that higher guidance is desireable for img2img processing // With the high prompt strength, increasing the steps isn't necessary //num_inference_steps: Math.max(parseInt(origRequest.num_inference_steps), 50), //DDIM may require more steps for better results //num_inference_steps: (isTurbo)? 10 : parseInt(origRequest.num_inference_steps) ), num_outputs: 1, use_vae_model: desiredVaeName(origRequest), sampler_name: desiredSamplerName(origRequest), seed: Math.floor(Math.random() * 10000000), }) newTaskRequest.seed = newTaskRequest.reqBody.seed; //Now, we allow the use of the same sampler when outpainting // newTaskRequest.reqBody.sampler_name = 'ddim'; //ensure img2img sampler change is properly reflected in log file newTaskRequest.batchCount = outpaintNumRuns; // assume user only wants one at a time to evaluate, if selecting one out of a batch newTaskRequest.numOutputsTotal = outpaintNumRuns; // " //If you have a lower-end graphics card, the below will automatically disable turbo mode for larger images. //Each person needs to test with different resolutions to find the limit of their card when using Balanced or modes other than 'low'. if (newTaskRequest.reqBody.width * newTaskRequest.reqBody.height >outpaintMaxTurboResolution) { //put max normal resolution here newTaskRequest.reqBody.vram_usage_level = 'low'; } delete newTaskRequest.reqBody.use_controlnet_model; //We're adding to the picture, and controlnet will try to make us conform to the old image. delete newTaskRequest.reqBody.control_filter_to_apply; delete newTaskRequest.reqBody.control_image; //newTaskRequest.reqBody.preserve_init_image_color_profile=true; //shouldn't be necessary, working from txt2img, and distorts colors //The comparison needs trimming, because the request box includes modifiers. If the first part of the prompts match, we assume nothing's changed, and move on. //If prompt has changed, ask if we should pick up the new value. Note that the new prompt will now include modifiers- previously, only the text-box. if (newTaskRequest.reqBody.prompt.substr(0,$("textarea#prompt").val().length)!=$("textarea#prompt").val()) { if (OutpaintItSettings.useChangedPrompt ) { newTaskRequest.reqBody.prompt=getPrompts()[0]; //promptField.value; // $("textarea#prompt").val(); }; } //we don't need to increase the steps so much, as with normal img2img in ScaleUp, as our prompt strength is high, using nearly all specified steps. if(isTurbo) { newTaskRequest.reqBody.num_inference_steps=Math.min(7,newTaskRequest.reqBody.num_inference_steps); } else { newTaskRequest.reqBody.num_inference_steps=Math.min(50,newTaskRequest.reqBody.num_inference_steps); } // //Use UI's prompt to allow changing to a different model, such as inpainting model, before outpainting. // if (OutpaintItSettings.useChangedModel) { // newTaskRequest.reqBody.use_stable_diffusion_model = $("#editor-settings #stable_diffusion_model")[0].dataset.path; // //newTaskRequest.reqBody.use_stable_diffusion_model =$("#editor-settings #stable_diffusion_model")[0].dataset.path; // newTaskRequest.use_vae_model = $("#editor-settings #vae_model")[0].dataset.path; // } newTaskRequest.reqBody.use_stable_diffusion_model=desiredModel; if (newTaskRequest.reqBody.width*newTaskRequest.reqBody.height>maxNoVaeTiling) { newTaskRequest.reqBody.enable_vae_tiling = true; //Force vae tiling on, if image is large } delete newTaskRequest.reqBody.use_upscale; //if previously used upscaler, we don't want to automatically do it again return newTaskRequest; } PLUGINS['IMAGE_INFO_BUTTONS'].push([ { html: 'OutpaintIt:', type: 'label', on_click: onOutpaintLabelClick, filter: onOutpaintLabelFilter}, { html: '', on_click: onOutpaintUpClick, filter: onOutpaintUpFilter }, { html: '', on_click: onOutpaintDownClick, filter: onOutpaintDownFilter }, { html: '', on_click: onOutpaintLeftClick, filter: onOutpaintLeftFilter }, { html: '', on_click: onOutpaintRightClick, filter: onOutpaintRightFilter }, { html: '', on_click: onOutpaintAllClick, filter: onOutpaintAllFilter } ]) var outpaintNumRuns = 1; function onOutpaintLabelClick(origRequest, image) { outpaintNumRuns++; if (outpaintNumRuns>5) { outpaintNumRuns=1; } //update current labels for (var index=0; indexOutpaint It Settings Reset Outpaint It Settings
  • OutpaintIt Settings
`; outpaintSettings.innerHTML = tempHTML; var editorSettings = document.getElementById('editor-settings'); editorSettings.parentNode.insertBefore(outpaintSettings, editorSettings.nextSibling); createCollapsibles(outpaintSettings); const icon = document.getElementById('reset-op-settings'); icon.addEventListener('click', outpaintItResetSettings); //Ensure switches match the settings (for the initial values), since "value=" in above HTML doesn't appear to work. outpaintItResetSettings(null); } setup(); })(); function setOutpaintItSettings() { OutpaintItSettings.useChangedPrompt = outpaintit_change_prompt.checked; OutpaintItSettings.useChangedModel = outpaintit_change_model.checked; OutpaintItSettings.useChangedSampler = outpaintit_change_sampler.checked; OutpaintItSettings.useExtendImage = outpaintit_extend_image.checked; //Extend image into noise area localStorage.setItem('OutpaintIt_Plugin_Settings', JSON.stringify(OutpaintItSettings)); //Store settings } //Sets the default values for the settings. function outpaintItResetSettings(reset) { let settings = JSON.parse(localStorage.getItem('OutpaintIt_Plugin_Settings')); if (settings == null || reset !=null) { //if settings not found, just set everything OutpaintItSettings.useChangedPrompt = false; OutpaintItSettings.useChangedModel = false; OutpaintItSettings.useChangedSampler = false; OutpaintItSettings.useExtendImage = false; //OutpaintItSettings.invertExtendImage = false; //TODO } else { //if settings found, but we've added a new setting, use a default value instead. (Not strictly necessary for this first group.) OutpaintItSettings.useChangedPrompt =settings.useChangedPrompt ?? false; OutpaintItSettings.useChangedModel =settings.useChangedModel ?? false; OutpaintItSettings.useChangedSampler =settings.useChangedSampler ?? false; OutpaintItSettings.useExtendImage =settings.useExtendImage ?? false; } localStorage.setItem('OutpaintIt_Plugin_Settings', JSON.stringify(OutpaintItSettings)); //Store settings //set the input fields outpaintit_change_prompt.checked = OutpaintItSettings.useChangedPrompt; outpaintit_change_model.checked = OutpaintItSettings.useChangedModel; outpaintit_change_sampler.checked = OutpaintItSettings.useChangedSampler; outpaintit_extend_image.checked = OutpaintItSettings.useExtendImage; }