diff --git a/README.md b/README.md index 44c09eb..70104e7 100644 --- a/README.md +++ b/README.md @@ -12,16 +12,22 @@ this is a completely vanilla javascript and html canvas outpainting convenience - i have never used html canvas for anything before and should try it out ## operation - you'll obviously need A1111's webUI installed before you can use this. - technically you can run it directly in browser as a bare `file://` protocol webpage but that's _really_ not recommended as you'll have to add `null` as an accepted domain to your `--cors-allow-origins` option which just seems like it's a visibly poor decision. - i therefore **strongly** recommend using a small webserver such as [simple-http-server](https://github.com/TheWaWaR/simple-http-server) if you don't have a local server already running somewhere. + ### prerequisities + you'll obviously need A1111's webUI installed before you can use this, thus you're presumed to have an operational python install up and running to boot. - 1. clone this repo or just literally download index.html and js/index.js manually and put 'em somewhere - 2. configure whatever local (host or network) webserver you're using to serve the index.html from this repo and execute it (instructions for configuring a web server are outside the scope of this remedial quickstart) - 3. modify your `webui-user.sh` or `webui-user.bat`'s `COMMANDLINE_ARGS` variable to contain ` --api --cors-allow-origins=http://127.0.0.1:1234` *(replacing 127.0.0.1:1234 with wherever you're hosting it on your local network if necessary)* + ### notes + technically you can run it directly in browser as a bare `file://` protocol webpage but that's _really_ not recommended as you'll have to add `null` as an accepted domain to your `--cors-allow-origins` option which just seems like it's a visibly poor decision. a deliciously simple launch script (thanks [@jasonmhead](https://github.com/jasonmhead)! (https://github.com/zero01101/openOutpaint/pull/1)) is included to pop up a teensy tiny python-based local webserver, however you may have to manually `chmod +x openOutpaint.sh` on mac/linux. the address http://127.0.0.1:3456 will be used as the host address for openOutpaint in the below quickstart; your local setup may use a different IP address or port. you can change the included launch script to point at a different port than 3456 if desired, as well. + +### quickstart + 1. clone this repo to your homelab's webserver (i mean who doesn't have a couple of those lying around?) or somewhere on your local pc + 2. configure your local webhost in your homelab to serve the newly cloned repo like the technological bastion you are, or simply run the included `openOutpaint.bat` on windows or `openOutpaint.sh` on mac/linux. + 3. modify your `webui-user.sh` or `webui-user.bat`'s `COMMANDLINE_ARGS` variable to contain ` --api --cors-allow-origins=http://127.0.0.1:3456` 4. execute your webui-user script and wait for it to be ready - 5. **SELECT AN INPAINTING MODEL (and associated VAE if applicable) IN WEBUI** - [runwayml/stable-diffusion-inpainting](https://huggingface.co/runwayml/stable-diffusion-inpainting) is recommended + 5. **APPLY THE FOLLOWING SETTINGS IN A1111 WEBUI ONCE IT IS READY:** + - select an inpainting checkpoint/model - ([runwayml/stable-diffusion-inpainting](https://huggingface.co/runwayml/stable-diffusion-inpainting) [3e16efc8] is recommended) + - set your `Inpainting conditioning mask strength` to `1` + - disable the `Apply color correction to img2img results to match original colors.` option (the last 2 options are found under the stable diffusion category in the settings tab by default unless you've already moved it to your quicksettings list, and if so, you know where to set them already) 6. open your locally-hosted web server, possibly appending `index.html` if it doesn't automatically serve that 7. update the host field if necessary to point at your stable diffusion API address, change my stupid prompts with whatever you want, click somewhere in the canvas, and wait 8. once an image appears*, click the `<` and `>` buttons at the bottom-left corner of the image to cycle through the others in the batch if you requested multiple (it defaults to 2 batch size, 2 batch count) - click `y` to choose one you like, or `n` to cancel that image generation batch outright and possibly try again @@ -33,21 +39,27 @@ this is a completely vanilla javascript and html canvas outpainting convenience ## //todo ### in order of "priority"/likelihood of me doing it -- [ ] comment basically everything that isn't self documenting +- [ ] comment basically everything that isn't self documenting (ongoing) +- [ ] overmask seam of img2img (https://www.reddit.com/r/StableDiffusion/comments/ys9lhq/kollai_an_infinite_multiuser_canvas_running_on/ivzygwk/?context=3) +- [ ] split out CSS to its own file (remedial cleanup task) +- [ ] split out JS to separation-of-concerns individual files (oh no) - [ ] add error handling for async/XHR POST in case of, yknow, errors - [ ] controls for the rest of API-available options (e.g. hires fix, inpaint fill modes, etc) - [ ] render progress spinner/bar -- [ ] figure out where that stupid 1-pixel offset is happening between approve/reject state and committing to an image, it doesn't affect output but it's _super_ obnoxious +- [ ] ~~smart crop downloaded image~~ (thanks [@Kalekki](https://github.com/Kalekki)! (https://github.com/zero01101/openOutpaint/pull/2)) +- [ ] import external image and scale/superimpose at will on canvas for in/outpainting +- [ ] "numpad" selector for determining how reticle is anchored against actual mouse cursor (currently works like a "5" [center] on the "numpad" paradigm) +- [ ] BUG: figure out where that stupid 1-pixel offset is happening between approve/reject state and committing to an image, it doesn't affect output but it's _super_ obnoxious - [ ] BUG: make erase mask actually work, enable the control if you dare -- [ ] infinite canvas - [ ] discrete size control for mask and target reticle, discrete x/y axes for reticle - [ ] floating/togglable menu leftnav bar with categorized/sensibly laid-out options -- [ ] smart crop downloaded image +- [ ] infinite canvas - [ ] global undo/redo - [ ] inpainting sketch tools - [ ] something actually similar to a "user interface", hopefully actually pleasant - [ ] eventually delete the generated mask display canvases at the bottom of the page, but they're useful for debugging canvas pixel offsets sometimes - [ ] see if i can use fewer canvases overall; seems wasteful, canvas isn't free yknow +- [ ] upscaling output canvas??? sure let's make 16k wallpapers that'll be neat - [ ] honestly probably refactor literally everything ## pull requests @@ -55,5 +67,13 @@ this is a completely vanilla javascript and html canvas outpainting convenience i am begging you, yes you personally reading this, please fix my horrible code and feel free to insult it, but i absolutely refuse to budge on no 3rd party libraries or dependencies, not even jquery, nothing. vanilla is a very complex and layered flavor if you give it a chance. ## sample -generated using 100% openOutpaint UI defaults except for switching to/from mask mode and changing scale factor to adjust the size of the mask blob, there's some neat stuff down there even if it disregarded the `people, humans, divers` negative prompt but in its defense there is only one singular person, human, diver in there, so according to the no homers club treatise of 1995 it's technically correct +generated using 100% openOutpaint UI defaults except for switching to/from mask mode and changing scale factor to adjust the size of the mask blob, there's some neat stuff down there even if it disregarded the `people, humans, divers` negative prompt but in its defense there is only one singular person, human, diver in there, so according to the no homers club treatise of 1995 it's technically correct _(see https://github.com/zero01101/openOutpaint/commit/92ab9d231542ea5f7a3c85563acf5cd3cb16a928 for attempted counterattack)_ ![fishies n stuff](docs/02-sample.png) + +## version history +- 0.0.1 - txt2img proof of concept +- 0.0.2 - img2img outpainting proof of concept +- 0.0.3 - image masking/img2img inpainting proof of concept +- 0.0.4 - batch size/batch count, approve/reject system implementations, other people are now allowed to see this thing https://github.com/zero01101/openOutpaint/commit/01f8c6ab3f49739439a0990d6f5f0967a9a0bf12 +- 0.0.4.1 - extremely minor revisions https://github.com/zero01101/openOutpaint/commit/02cb01ac062ef93878ff4161eabcedfa8e125be6 +- 0.0.4.2 - pull requests (<3), downloaded images now have a timestamped name, css breakout because hopefully this will become halfway attractive enough to benefit from non-inline stylesheets \ No newline at end of file diff --git a/css/index.css b/css/index.css new file mode 100644 index 0000000..01e4014 --- /dev/null +++ b/css/index.css @@ -0,0 +1,74 @@ +.container { + position: relative; +} + +.backgroundCanvas { + background-color: #ccc; +} + +.maskPaintCanvas { + border: 3px dotted #993355C0 +} + +.overlayCanvas { + border: 1px solid #F00; +} + +.tempCanvas { + border: 3px dotted #007AFFC0; +} + +.targetCanvas { + border: 2px dashed #0F0; +} + +.canvas { + border: 2px dotted #00F; +} + +.mainHSplit { + display: grid; + grid-template-columns: 1fr; + grid-template-rows: repeat(2, 1fr); + grid-column-gap: 5px; + grid-row-gap: 5px; +} + +.uiWrapper { + display: grid; + grid-template-columns: 1fr 15fr; + grid-template-rows: 1fr; + grid-column-gap: 5px; + grid-row-gap: 5px; +} + +.canvasHolder { + position: relative; + width: 2560px; + height: 1440px; +} + +.mainCanvases { + position: absolute; + top: 0px; + left: 0px; + width: 2560px; + height: 1440px; +} + +.masks { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-template-rows: 1fr; + grid-column-gap: 0px; + grid-row-gap: 0px; +} + +.maskCanvas { + position: absolute; +} + +.initImgCanvas { + position: absolute; + left: 600px; +} \ No newline at end of file diff --git a/index.html b/index.html index e80a1b0..3fb82c1 100644 --- a/index.html +++ b/index.html @@ -3,84 +3,8 @@ - openOutpaint 0.0.4.1 - - + openOutpaint 0.0.4.2 + diff --git a/js/index.js b/js/index.js index 5ea05d6..34e3d72 100644 --- a/js/index.js +++ b/js/index.js @@ -537,39 +537,47 @@ function drawBackground() { function downloadImage() { var link = document.createElement('a'); - link.download = 'image.png'; - link.href = cropCanvas(imgCanvas).toDataURL('image/png'); - link.click(); + link.download = new Date().toISOString().slice(0, 19).replace('T', ' ').replace(':', ' ') + ' openOutpaint image.png'; + croppedCanvas = cropCanvas(imgCanvas); + if (croppedCanvas != null) { + link.href = croppedCanvas.toDataURL('image/png'); + link.click(); + } } function cropCanvas(sourceCanvas) { var w = sourceCanvas.width; var h = sourceCanvas.height; - var pix = {x:[], y:[]}; + var pix = { x: [], y: [] }; var imageData = sourceCanvas.getContext('2d').getImageData(0, 0, w, h); var x, y, index; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { index = (y * w + x) * 4; - if (imageData.data[index+3] > 0) { + if (imageData.data[index + 3] > 0) { pix.x.push(x); pix.y.push(y); } } } - pix.x.sort(function(a,b){return a-b}); - pix.y.sort(function(a,b){return a-b}); - var n = pix.x.length-1; + pix.x.sort(function (a, b) { return a - b }); + pix.y.sort(function (a, b) { return a - b }); + var n = pix.x.length - 1; w = pix.x[n] - pix.x[0]; h = pix.y[n] - pix.y[0]; - - var cut = sourceCanvas.getContext('2d').getImageData(pix.x[0], pix.y[0], w, h); - var cutCanvas = document.createElement('canvas'); - cutCanvas.width = w; - cutCanvas.height = h; - cutCanvas.getContext('2d').putImageData(cut, 0, 0); + try { + var cut = sourceCanvas.getContext('2d').getImageData(pix.x[0], pix.y[0], w, h); + var cutCanvas = document.createElement('canvas'); + cutCanvas.width = w; + cutCanvas.height = h; + cutCanvas.getContext('2d').putImageData(cut, 0, 0); + } catch (ex) { + // probably empty image + //TODO confirm edge cases? + cutCanvas = null; + } return cutCanvas; } diff --git a/webui.bat b/openOutpaint.bat similarity index 100% rename from webui.bat rename to openOutpaint.bat diff --git a/openOutpaint.sh b/openOutpaint.sh new file mode 100644 index 0000000..3502150 --- /dev/null +++ b/openOutpaint.sh @@ -0,0 +1 @@ +python -m http.server 3456 \ No newline at end of file