-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
docbot
committed
May 13, 2024
1 parent
c56eb98
commit 4cc21d0
Showing
5 changed files
with
284 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
scripts.drawthings.ai |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
[ | ||
{ | ||
"name": "Creative Upscale", | ||
"file": "creative-upscale.js", | ||
"author": "Liu Liu", | ||
"description": "Generate up to 4x upscaled image from the input with crisp details.", | ||
"tags": [ | ||
"image-to-image" | ||
] | ||
}, | ||
{ | ||
"name": "SD Ultimate Upscale", | ||
"file": "sd-ultimate-upscale.js", | ||
"author": "Gong Chen", | ||
"description": "Generate high-quality, high-resolution images from low-resolution inputs, while preserving fine details and textures.", | ||
"tags": [ | ||
"image-to-image" | ||
] | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
//@api-1.0 | ||
|
||
const userSelection = requestFromUser("User Selection", "", function() { | ||
return [ | ||
this.section("Upscale Factor", "This determines many times we should upscale the image. For example, 400% requires us to upscale the image twice.", [ | ||
this.segmented(1, ["200%", "400%"]) | ||
]) | ||
]; | ||
}); | ||
|
||
const configuration = pipeline.configuration; | ||
|
||
pipeline.downloadBuiltins(["4x_ultrasharp_f16.ckpt"]); | ||
|
||
configuration.strength = 0; | ||
configuration.upscaler = "4x UltraSharp"; | ||
pipeline.run({configuration: configuration}); | ||
|
||
const tileSrc = canvas.saveImageSrc(true); | ||
|
||
pipeline.downloadBuiltins(["controlnet_tile_1.x_v1.1_f16.ckpt", "juggernaut_reborn_q6p_q8p.ckpt", "add_more_details__detail_enhancer___tweaker__lora_f16.ckpt", "sdxl_render_v2.0_lora_f16.ckpt", "tcd_sd_v1.5_lora_f16.ckpt"]); | ||
|
||
const imageRect = canvas.boundingBox; | ||
|
||
const baseZoom = canvas.canvasZoom; | ||
|
||
configuration.width = configuration.width * 2; | ||
configuration.height = configuration.height * 2; | ||
canvas.updateCanvasSize(configuration); | ||
canvas.canvasZoom = baseZoom * 2; | ||
canvas.moveCanvas(imageRect.x, imageRect.y); | ||
|
||
configuration.steps = 8; | ||
configuration.tiledDecoding = true; | ||
configuration.decodingTileWidth = 1024; | ||
configuration.decodingTileHeight = 1024; | ||
configuration.decodingTileOverlap = 128; | ||
configuration.tiledDiffusion = true; | ||
configuration.diffusionTileWidth = 1024; | ||
configuration.diffusionTileHeight = 1024; | ||
configuration.diffusionTileOverlap = 128; | ||
configuration.sampler = SamplerType.TCD; | ||
configuration.stochasticSamplingGamma = 0.3; | ||
configuration.upscaler = null; | ||
|
||
configuration.model = "juggernaut_reborn_q6p_q8p.ckpt"; | ||
configuration.strength = 0.4; | ||
configuration.loras = [{"file": "add_more_details__detail_enhancer___tweaker__lora_f16.ckpt", "weight": 0.6}, {"file": "sdxl_render_v2.0_lora_f16.ckpt", "weight": 1}, {"file": "tcd_sd_v1.5_lora_f16.ckpt", "weight": 1}]; | ||
const tile = pipeline.findControlByName("Tile (SD v1.x, ControlNet 1.1)"); | ||
tile.weight = 0.5; | ||
configuration.controls = [tile]; | ||
|
||
pipeline.run({configuration: configuration, prompt: "masterpiece, best quality, highres"}); | ||
|
||
if (userSelection[0] > 0) { // This allows us to upscale again to 400%. | ||
configuration.width = configuration.width * 2; | ||
configuration.height = configuration.height * 2; | ||
canvas.updateCanvasSize(configuration); | ||
canvas.canvasZoom = baseZoom * 4; | ||
canvas.moveCanvas(imageRect.x, imageRect.y); | ||
|
||
canvas.loadCustomLayerFromSrc(tileSrc); | ||
|
||
pipeline.run({configuration: configuration, prompt: "masterpiece, best quality, highres"}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
//@api-1.0 | ||
// | ||
// "Scripts" is a way to enhance Draw Things experience with custom JavaScript snippets. Over time, | ||
// we will enhance Scripts with more APIs and do neccessary modifications on the said APIs, but we | ||
// will also keep the API stable. In particular, you can mark your script with a particular API | ||
// version and it will make sure we use the proper API toolchain to execute the script even in | ||
// the future some of the APIs changed. | ||
// | ||
// Existing APIs: | ||
// | ||
// We currently provides three global objects for you to interact with: `canvas`, `pipeline` and `filesystem`. | ||
// | ||
// `canvas` is the object to manipulate the existing infinite canvas on Draw Things interface. It | ||
// supports the following methods: | ||
// | ||
// `canvas.boundingBox`: return the bounding box of existing images collectively on the canvas. | ||
// `canvas.clear()`: remove all images on the canvas, same effect as you tap on the "New Canvas" button. | ||
// `canvas.createMask(width, height, value)`: create a mask with a given size and fill-in value. | ||
// `canvas.currentMask`: return the current mask on the view, you can further manipulate the mask through `Mask` APIs. | ||
// `canvas.loadImage(file)`: load image from a file (a path). | ||
// `canvas.moveCanvas(left, top)`: move the canvas to a particular coordinate, these coordinates work as if the zoom scale is 1x. | ||
// `canvas.saveImage(file, visibleRegionOnly)`: save the image currently on canvas to a file. | ||
// `canvas.canvasZoom`: Set / get the zoom scale of the canvas. | ||
// `canvas.topLeftCorner`: return the coordinate the canvas currently at, these coordinates work as if the zoom scale is 1x. | ||
// `canvas.updateCanvasSize(configuration)`: extract the size from configuration object and then sync the canvas with that size. | ||
// | ||
// `pipeline` is the object to run the image generation pipeline. It supports the following methods: | ||
// | ||
// `pipeline.configuration`: extract the current configuration, whether it is on screen or after pipeline.run. | ||
// `pipeline.findControlByName(name)`: using the display name to find a particular control. | ||
// `pipeline.findLoRAByName(name)`: using the display name to find a particular LoRA. | ||
// `pipeline.prompts`: return prompt and negativePrompt currently on the screen. | ||
// `pipeline.run({prompt: null, negativePrompt: null, configuration: configuration, mask: null})`: run image generation through | ||
// this API. You can optionally provides prompt / negativePrompt, or it can take prompt from the screen. mask can be provided | ||
// optionally too and sometimes it can be helpful. | ||
// | ||
// `filesystem` provides a simple access to the underlying file system. Note that Draw Things is | ||
// sandboxed so we can only have access to particular directories within the user file system. | ||
// | ||
// `filesystem.pictures.path`: get the path of the pictures folder. It is the system Pictures folder on macOS, and a Pictures | ||
// folder under Draw Things within Files app for iOS / iPadOS. | ||
// `filesystem.pictures.readEntries`: enumerate all the files under the pictures folder. | ||
// | ||
// This script upscales the original image to 512x512 tiles and fixes the seams. | ||
// This script assumes the original image is at 1x zoom level. | ||
// The configuration parameters in this script may need tuning to improve final image. | ||
// Author: @Gooster | ||
|
||
const USE_UPSCALER = "Real-ESRGAN X2+"; | ||
const UPSCALE_FACTOR = 2; | ||
|
||
const TILE_STRENGTH = 0.7; | ||
const INPAINTING_STRENGTH = 0.7; | ||
const MASK_BLUR = 6; | ||
const MIN_OVERLAP = 48; | ||
const TILE_SIZE = 512; | ||
|
||
const configuration = pipeline.configuration; | ||
configuration.maskBlur = MASK_BLUR; | ||
|
||
const imageRect = canvas.boundingBox; | ||
|
||
// initial upscale | ||
canvas.canvasZoom = 1; | ||
if (USE_UPSCALER) { | ||
pipeline.downloadBuiltin(USE_UPSCALER); | ||
configuration.strength = 0; | ||
configuration.upscaler = USE_UPSCALER; | ||
canvas.moveCanvas(imageRect.x, imageRect.y); | ||
// generate the upscaled image | ||
pipeline.run({ configuration: configuration }); | ||
} | ||
pipeline.downloadBuiltin("controlnet_tile_1.x_v1.1_f16.ckpt"); | ||
pipeline.downloadBuiltin("controlnet_inpaint_1.x_v1.1_f16.ckpt"); | ||
// tiling over the image to upscale | ||
configuration.strength = TILE_STRENGTH; | ||
tiledUpscale(configuration, imageRect, UPSCALE_FACTOR); | ||
canvas.moveCanvas(imageRect.x, imageRect.y); | ||
canvas.canvasZoom = 1; | ||
|
||
// tiled upscale support integer zoom levels | ||
function tiledUpscale(configuration, imageRect, zoom) { | ||
const minOverlap = MIN_OVERLAP; | ||
// set zoom level and reconfigure the canvas dimensions to tile size | ||
configuration.upscaler = null; | ||
configuration.width = configuration.height = TILE_SIZE; | ||
canvas.updateCanvasSize(configuration); | ||
canvas.canvasZoom = zoom; | ||
canvas.moveCanvas(imageRect.x, imageRect.y); | ||
const numTiles = { | ||
x: imageRect.width * zoom / configuration.width, | ||
y: imageRect.height * zoom / configuration.height | ||
} | ||
const tileSize = Math.floor(imageRect.width / numTiles.x) | ||
numTiles.x = Math.ceil(numTiles.x + (minOverlap * (numTiles.x - 1)) / configuration.width) | ||
numTiles.y = Math.ceil(numTiles.y + (minOverlap * (numTiles.y - 1)) / configuration.height) | ||
console.log(`Number of tiles: ${numTiles.x * numTiles.y}`); | ||
canvas.moveCanvas(imageRect.x, imageRect.y); | ||
const generatedRows = []; | ||
let previousCoordinates = { x: imageRect.x, y: imageRect.y }; | ||
let currentCoordinates = previousCoordinates; | ||
|
||
// tiling over the image | ||
for (let y = 0; y < numTiles.y; y++) { | ||
let currentRow = new Rectangle(0, 0, 0, 0) | ||
for (let x = 0; x < numTiles.x; x++) { | ||
// restore tile control and strength for each tile | ||
const tileControl = pipeline.findControlByName("Tile (SD v1.x, ControlNet 1.1)"); | ||
configuration.controls = [tileControl]; | ||
configuration.strength = TILE_STRENGTH; | ||
currentCoordinates = { | ||
x: imageRect.x + Math.floor(x * (tileSize - minOverlap)), | ||
y: imageRect.y + Math.floor(y * (tileSize - minOverlap)) | ||
}; | ||
if (x == 0) { | ||
currentRow.x = currentCoordinates.x; | ||
currentRow.y = currentCoordinates.y; | ||
} | ||
// the last tile should match the x, y edge of the image | ||
if (x == numTiles.x - 1) currentCoordinates.x = imageRect.x + imageRect.width - tileSize; | ||
if (y == numTiles.y - 1) currentCoordinates.y = imageRect.y + imageRect.height - tileSize; | ||
console.log(`Upscaling tile ${x + y * numTiles.x} of ${numTiles.x * numTiles.y}`); | ||
// add mask for tiles other than the first | ||
let mask = new Rectangle(currentCoordinates.x, currentCoordinates.y, tileSize, tileSize); | ||
if (x != 0) { | ||
// exclude the current row | ||
mask = mask.exclude(currentRow); | ||
} | ||
if (y != 0) { | ||
// exclude the row above | ||
mask = mask.exclude(generatedRows[y - 1]); | ||
} | ||
// convert mask coordinate to tile local coordinates | ||
mask.x -= currentCoordinates.x; | ||
mask.y -= currentCoordinates.y; | ||
mask.scale(configuration.width / tileSize); | ||
// ensure mask is the same size as the configuration | ||
const generateRegion = canvas.createMask(configuration.width, configuration.height, 0); | ||
generateRegion.fillRectangle( | ||
mask.x, | ||
mask.y, | ||
mask.width, | ||
mask.height, 2); | ||
canvas.moveCanvas(currentCoordinates.x, currentCoordinates.y) | ||
pipeline.run({ configuration: configuration, mask: generateRegion }); | ||
// seams are created at the right and bottom edge of each previous tile we can fix the seam at the next tile generation | ||
configuration.strength = INPAINTING_STRENGTH; | ||
const inpaintControl = pipeline.findControlByName("Inpainting (SD v1.x, ControlNet 1.1)"); | ||
configuration.controls = [inpaintControl]; | ||
const inpaintRegion = canvas.createMask(configuration.width, configuration.height, 0); | ||
let edgePolisher = 5; | ||
if (y > 0) { | ||
// after the first row always fill the previous row's bottom edge | ||
edgePolisher = x === 0 ? 0 : 5; | ||
const inpaintRect = new Rectangle( | ||
edgePolisher, | ||
mask.y - Math.floor(minOverlap / 2), | ||
configuration.width - edgePolisher, | ||
minOverlap); | ||
inpaintRegion.fillRectangle( | ||
inpaintRect.x, | ||
inpaintRect.y, | ||
inpaintRect.width, | ||
inpaintRect.height, | ||
2 | ||
) | ||
} | ||
if (x > 0) { | ||
edgePolisher = y === 0 ? 0 : 5; | ||
const inpaintRect = new Rectangle( | ||
mask.x - Math.floor(minOverlap / 2), | ||
edgePolisher, | ||
minOverlap, | ||
configuration.height - edgePolisher); | ||
inpaintRegion.fillRectangle( | ||
inpaintRect.x, | ||
inpaintRect.y, | ||
inpaintRect.width, | ||
inpaintRect.height, | ||
2 | ||
) | ||
} | ||
// skip over the first tile that doesn't have seams yet | ||
canvas.moveCanvas(currentCoordinates.x, currentCoordinates.y) | ||
if (x != 0 || y != 0) { | ||
console.log(`Inpainting seams on tile ${x + y * numTiles.x} of ${numTiles.x * numTiles.y}`); | ||
pipeline.run({ configuration: configuration, mask: inpaintRegion }); | ||
} | ||
previousCoordinates = currentCoordinates; | ||
currentRow = Rectangle.union(currentRow, new Rectangle(currentCoordinates.x, currentCoordinates.y, tileSize, tileSize)); | ||
} | ||
generatedRows.push(currentRow); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"creative-upscale": "65ee6cf778a907c57f8eb2678d45257915bef2439bebd710ae2a60d5d34bd305", | ||
"sd-ultimate-upscale": "74d7ef28e648513340ab02ada3aa4cda147854df18de334e66a0f803c62c454c" | ||
} |