Compare commits

...

78 Commits
1.0 ... main

Author SHA1 Message Date
vahid khroasani 60adae2252
Merge pull request #120 from D0n-A/main
Update ui.py
2024-05-17 15:39:23 +04:00
Don-A 515b4d6cac
Update ui.py
Fix warning "GradioDeprecationWarning: The `style` method is deprecated. Please set these arguments in the constructor instead"

Signed-off-by: Don-A <38649081+D0n-A@users.noreply.github.com>
2024-03-11 20:50:21 +03:00
vahid K. nejad 74338a118d Hotfix: UI correction 2024-03-06 07:34:37 +03:00
vahid khroasani 70d9ee4629
Merge pull request #119 from RobeSantoro/fix-issue-#118
fix: update unpacking of create_output_panel return value
2024-03-06 07:14:56 +03:00
vahid khroasani cfbd32363f
Apply suggestions from code review
Co-authored-by: Loki's Wager <32408858+LokiWager@users.noreply.github.com>
Signed-off-by: vahid khroasani <62482657+v8hid@users.noreply.github.com>
2024-03-06 07:13:08 +03:00
RobeSantoro ac0d51818b fix: update unpacking of create_output_panel return value 2024-03-03 19:43:01 +01:00
vahid khroasani d6461e7daa
Merge pull request #113 from v8hid/develop
Minor fix
2023-09-05 04:11:31 +03:00
vahid khroasani 4edb1729c9
Merge pull request #112 from v8hid/main
merge main
2023-09-05 04:09:52 +03:00
vahid khroasani 358c7e954d
Merge pull request #109 from alexbofa/main
fixes #110
2023-09-05 03:40:15 +03:00
alexbofa 8259681774
Update ui.py
Signed-off-by: alexbofa <58225118+alexbofa@users.noreply.github.com>
2023-08-31 22:33:13 +03:00
vahid khroasani 07969fc78c
Merge pull request #89 from WALL-E/main
Update install.py
2023-05-23 22:14:21 +03:00
ZZ 0b28b24200
Update install.py
Signed-off-by: ZZ <zzgigi2003@163.com>
2023-05-11 15:29:23 +00:00
vahid khroasani 1139764bf7
Merge pull request #86 from v8hid/develop
interrup btn fix
2023-05-03 21:08:52 +04:00
vahid K. nejad 8cedb40787 interrup btn fix 2023-05-03 21:08:15 +04:00
vahid khroasani baec28cf5e
Merge pull request #81 from v8hid/develop
HotFix
2023-05-02 16:07:22 +04:00
vahid khroasani 79aa8f40ab
Merge pull request #80 from Oncorporation/develop
Fix typo in static variables
2023-05-02 15:37:45 +04:00
Charles Fettinger a5e96810ec Fix typo in static variables 2023-05-02 04:33:01 -07:00
vahid khroasani d7263b3f38
Merge pull request #78 from v8hid/develop
v1.2 


    Added common prompt prefix and suffix for better user experience.
    Improved frame correction and enhancement with mask_blur and non-inpainting models.
    All main frames will be shown as a gallery view in output.
    Updated default parameter values.
    Fixed bugs related to prompts import.
    Made improvements to UI parameter Names and Frame problem (It's gone).
    Refactored code for better maintenance.
2023-05-02 11:25:49 +04:00
vahid khroasani aecab9c242
Merge branch 'main' into develop
Signed-off-by: vahid khroasani <62482657+v8hid@users.noreply.github.com>
2023-05-02 11:21:45 +04:00
vahid K. nejad 422e140b10 fixes, 0.5 for upscale factor slider steps 2023-05-02 10:22:44 +04:00
vahid K. nejad 842c5b8b4b typo 2023-05-02 09:56:13 +04:00
vahid K. nejad 2ed87c1eff Remove unnecessary params and change default params 2023-05-02 09:50:42 +04:00
GeorgLegato 81636a081b addedd warning on SafeCollection setting 2023-05-01 22:44:27 +02:00
GeorgLegato 38355dfab8 fix and clean json handling, commonX considered, headers not 0,1 anymore 2023-05-01 22:17:41 +02:00
GeorgLegato 90a5aab9dd fix; remeaining old keywords in JSON; common vs pre/post 2023-05-01 21:45:10 +02:00
GeorgLegato fdb8eba2fa stupid coding, not using a const string, i mistyped, of course. 2023-05-01 18:20:50 +02:00
vahid khroasani 6380e1ab0d
Merge pull request #75 from v8hid/validate_table_on_generate
Validate table on generate
2023-05-01 19:11:56 +04:00
vahid khroasani fe16a68e9f
Merge pull request #76 from v8hid/frame-correction
Frame correction
2023-05-01 16:33:13 +04:00
vahid khroasani 119f53a466
Merge branch 'develop' into frame-correction
Signed-off-by: vahid khroasani <62482657+v8hid@users.noreply.github.com>
2023-05-01 16:32:59 +04:00
vahid K. nejad bf41a308c3 Printing path to saved video, typo fix 2023-05-01 16:29:32 +04:00
vahid K. nejad a50c104650 default prompts changed 2023-05-01 16:04:50 +04:00
GeorgLegato 21ace4d31b Merge branch 'validate_table_on_generate' of https://github.com/v8hid/infinite-zoom-automatic1111-webui into validate_table_on_generate 2023-05-01 01:29:57 +02:00
GeorgLegato 78d1e48856 changed wording to help users understand concept very easily. disabling generate button on invalid table 2023-05-01 01:29:40 +02:00
GeorgLegato 430b2d2310 fix json complete. do not accept headers 2023-05-01 01:29:40 +02:00
GeorgLegato d8703520ec added check 2023-05-01 01:29:40 +02:00
GeorgLegato 700b1c9560 changed wording to help users understand concept very easily. disabling generate button on invalid table 2023-05-01 01:29:11 +02:00
GeorgLegato 443b3256f5 fix json complete. do not accept headers 2023-05-01 01:07:57 +02:00
GeorgLegato 7591a96155
hotfix: fix prompts neg/post/pre
Signed-off-by: GeorgLegato <bulbul@onlinehome.de>
2023-04-30 22:40:52 +02:00
GeorgLegato b7a0cb318a added check 2023-04-30 22:39:31 +02:00
GeorgLegato 8da2fb1ba7
Hotfix: copy paste, post/preprompts on main level of jason
Signed-off-by: GeorgLegato <bulbul@onlinehome.de>
2023-04-30 22:38:51 +02:00
GeorgLegato 4114c85b97 Total Steps minimun is now 1 (init+1x outpaint
)
2023-04-30 21:57:05 +02:00
GeorgLegato 519944ddd0 Fix steps2, promp1 and init img 2023-04-30 21:43:50 +02:00
vahid K. nejad 6369ecc5b6 fix gallery view 2023-04-30 22:29:42 +04:00
vahid K. nejad a3e59697bd Corrected frame quality enhanced
changing round to math.ceil for interpolation
2023-04-30 16:50:34 +04:00
vahid khroasani 28a1a0f40c
Update issue templates 2023-04-29 15:45:36 +04:00
vahid khroasani 5910ee7b24
Create CODE_OF_CONDUCT.md
Signed-off-by: vahid khroasani <62482657+v8hid@users.noreply.github.com>
2023-04-29 15:35:34 +04:00
GeorgLegato 14b2f4edaa shorter names in json schema (neg/pre/postPrompt) 2023-04-28 21:49:41 +02:00
GeorgLegato c284fa24e5 changed JSON schema: negPrompt, prePrompt, postPrompt, less name length 2023-04-28 21:48:56 +02:00
vahid K. nejad e72cfcd33d Seperating the outpaint and interpolation, and apply frame correction 2023-04-28 12:28:17 +04:00
vahid K. nejad bb7d7082d7 Frame correction starting point 2023-04-28 06:58:56 +04:00
GeorgLegato f48d29b1be filtering in settings paint/inpainting models in dropdown 2023-04-28 03:22:54 +02:00
vahid K. nejad 44114c2252 hiding exit image for now 2023-04-27 14:19:53 +04:00
vahid K. nejad e41d103791 fixing import 2023-04-27 12:36:04 +04:00
GeorgLegato 90dbc3b2f0 added setting to collect all images etc into one folder. fixed default output path folder in settings default 2023-04-27 04:22:40 +02:00
GeorgLegato 18832be8ce exporting JSON with pretty printing 2023-04-27 02:04:35 +02:00
GeorgLegato 83a21b5155
Merge pull request #59 from v8hid/commonPrompt
Common prompt (incl json schema refactory)
2023-04-27 01:19:26 +02:00
vahid khroasani 8ba3354b04
Merge pull request #68 from v8hid/GeorgLegato-readme_auto1111_AND_Vlad
Update README.md to support VLAD as well
2023-04-27 00:00:57 +04:00
GeorgLegato bf3a4b326c
Update README.md
Signed-off-by: GeorgLegato <bulbul@onlinehome.de>
2023-04-26 19:32:28 +02:00
GeorgLegato 00b2eed6e7 slightly layout improves, hinting 2023-04-26 06:16:57 +02:00
GeorgLegato 1c44b0b41c added Prefix,Suffix, Toolltips, Accordions for sectionizing groups 2023-04-26 05:49:16 +02:00
GeorgLegato 3a481c06ee fix putprompts keep table on invalid input 2023-04-25 01:03:21 +02:00
GeorgLegato cf25a1a5aa disabled gr.error, fixed double main prompt merge glitch 2023-04-25 00:49:11 +02:00
GeorgLegato 2f3858eba2 headers optional 2023-04-25 00:25:20 +02:00
GeorgLegato eb6f532da2 refactor jsonutil 2023-04-25 00:24:48 +02:00
GeorgLegato 5dde17ad39 refactor jsonschema 2023-04-25 00:24:37 +02:00
vahid khroasani a730c8a271
Update README.md 2023-04-25 02:17:32 +04:00
GeorgLegato b0cb75399e added common prompt; todo convert old json prompts to 1.1 schema 2023-04-24 14:12:00 +02:00
GeorgLegato 365bb7e82a
Merge pull request #56 from jmsether/develop
Fixing grid images causing ffmpeg to crash
2023-04-24 02:25:01 +02:00
jmsether f2764672de Fixed all areas that were triggering with a array of zero crash. 2023-04-23 18:03:56 -06:00
jmsether 7f56865112 So apparently SD can return an array of zero... who knew, Implemented check for that as well. Interupting generation should no longer produce crashes 2023-04-23 16:33:38 -06:00
jmsether 38b64d866e Fixed rare crash bug when interupting generation 2023-04-23 16:29:48 -06:00
jmsether f933aeb284 Fixing grid images causing ffmpeg to crash 2023-04-23 03:46:11 -06:00
vahid khroasani f48719000f
Refactor (#54)
A little bit of cleaning up the code!
2023-04-23 07:20:06 +04:00
vahid khroasani fe7971a164
Merge pull request #49 from Oncorporation/dev
Implement Infinite Zoom Exit Image
2023-04-22 21:08:44 +04:00
vahid khroasani 49b2bc9d9b
Update infinite-zoom.py
stupid "," typo

Signed-off-by: vahid khroasani <62482657+v8hid@users.noreply.github.com>
2023-04-22 05:41:33 +04:00
Charles Fettinger f581d8e8dd Implement Infinite Zoom Exit Image
update gitignore
specify seed
2023-04-21 00:46:00 -07:00
GeorgLegato fafd087556 Merge branch 'develop' of https://github.com/v8hid/infinite-zoom-automatic1111-webui into develop 2023-04-21 06:00:06 +02:00
GeorgLegato ec0f6bc82e Merge branches 'main' and 'develop' of https://github.com/v8hid/infinite-zoom-automatic1111-webui into develop 2023-04-21 04:05:50 +02:00
21 changed files with 1562 additions and 940 deletions

29
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,29 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Enter these inputs '....'
2. Click on '....'
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots of error**
If applicable, add screenshots to help explain your problem. Or just dump the error.
**Desktop (please complete the following information):**
- OS: [e.g. Windows]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[FEATURE]"
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

1
.gitignore vendored
View File

@ -129,3 +129,4 @@ dmypy.json
.pyre/
.vscode/settings.json
.DS_Store
/.vs

128
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
me@vahid.cloud.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@ -1,4 +1,4 @@
# Infinite Zoom extension for [Stable Diffusion WebUI](https://github.com/AUTOMATIC1111/stable-diffusion-webui/).
# Infinite Zoom extension for [Stable Diffusion WebUI](https://github.com/AUTOMATIC1111/stable-diffusion-webui/).
<p align="center">
<a href="https://discord.gg/v2nHqSrWdW">
@ -6,7 +6,7 @@
</a>
</p>
This is an extension for the AUTOMATIC1111's webui that allows users to create infinite zoom effect videos using stable diffusion outpainting method.
This is an extension for the AUTOMATIC1111's (and Vladmandic´s) webui that allows users to create infinite zoom effect videos using stable diffusion outpainting method.
<p align="center">
<img src="https://user-images.githubusercontent.com/62482657/233385585-82d7157e-1438-4cf8-b805-220d96bbbe31.gif" width="332" height="188" />
</p>
@ -112,4 +112,4 @@ Contributions are welcome! Please follow these guidelines:
1. Fork the repository.
2. Make your changes and commit them.
3. Submit a pull request to the main repository.
3. Make sure to submit the pull request to the develop repository.

View File

@ -2,5 +2,5 @@ import launch
if not launch.is_installed("imageio"):
launch.run_pip("install imageio", "requirements 0 for Infinite-Zoom")
if not launch.is_installed("imageio-ffmpeg"):
if not launch.is_installed("imageio_ffmpeg"):
launch.run_pip("install imageio-ffmpeg", "requirements 1 for Infinite-Zoom")

View File

@ -1,2 +1,2 @@
from .image import shrink_and_paste_on_blank
from .video import write_video
# from .ui import on_ui_tabs
# from .settings import on_ui_settings

0
iz_helpers/extra.py Normal file
View File

126
iz_helpers/helpers.py Normal file
View File

@ -0,0 +1,126 @@
import math
import os
import modules.shared as shared
import modules.sd_models
import gradio as gr
from scripts import postprocessing_upscale
from .prompt_util import readJsonPrompt
import asyncio
def fix_env_Path_ffprobe():
envpath = os.environ["PATH"]
ffppath = shared.opts.data.get("infzoom_ffprobepath", "")
if ffppath and not ffppath in envpath:
path_sep = ";" if os.name == "nt" else ":"
os.environ["PATH"] = envpath + path_sep + ffppath
def closest_upper_divisible_by_eight(num):
if num % 8 == 0:
return num
else:
return math.ceil(num / 8) * 8
def load_model_from_setting(model_field_name, progress, progress_desc):
# fix typo in Automatic1111 vs Vlad111
if hasattr(modules.sd_models, "checkpoint_alisases"):
checkPList = modules.sd_models.checkpoint_alisases
elif hasattr(modules.sd_models, "checkpoint_aliases"):
checkPList = modules.sd_models.checkpoint_aliases
else:
raise Exception(
"This is not a compatible StableDiffusion Platform, can not access checkpoints"
)
model_name = shared.opts.data.get(model_field_name)
if model_name is not None and model_name != "":
checkinfo = checkPList[model_name]
if not checkinfo:
raise NameError(model_field_name + " Does not exist in your models.")
if progress:
progress(0, desc=progress_desc + checkinfo.name)
modules.sd_models.load_model(checkinfo)
def do_upscaleImg(curImg, upscale_do, upscaler_name, upscale_by):
if not upscale_do:
return curImg
# ensure even width and even height for ffmpeg
# if odd, switch to scale to mode
rwidth = round(curImg.width * upscale_by)
rheight = round(curImg.height * upscale_by)
ups_mode = 2 # upscale_by
if (rwidth % 2) == 1:
ups_mode = 1
rwidth += 1
if (rheight % 2) == 1:
ups_mode = 1
rheight += 1
if 1 == ups_mode:
print(
"Infinite Zoom: aligning output size to even width and height: "
+ str(rwidth)
+ " x "
+ str(rheight),
end="\r",
)
pp = postprocessing_upscale.scripts_postprocessing.PostprocessedImage(curImg)
ups = postprocessing_upscale.ScriptPostprocessingUpscale()
ups.process(
pp,
upscale_mode=ups_mode,
upscale_by=upscale_by,
upscale_to_width=rwidth,
upscale_to_height=rheight,
upscale_crop=False,
upscaler_1_name=upscaler_name,
upscaler_2_name=None,
upscaler_2_visibility=0.0,
)
return pp.image
async def showGradioErrorAsync(txt, delay=1):
await asyncio.sleep(delay) # sleep for 1 second
raise gr.Error(txt)
def putPrompts(files):
try:
with open(files.name, "r") as f:
file_contents = f.read()
data = readJsonPrompt(file_contents,False)
return [
gr.Textbox.update(data["prePrompt"]),
gr.DataFrame.update(data["prompts"]),
gr.Textbox.update(data["postPrompt"]),
gr.Textbox.update(data["negPrompt"])
]
except Exception:
print(
"[InfiniteZoom:] Loading your prompt failed. It seems to be invalid. Your prompt table is preserved."
)
# error only be shown with raise, so ui gets broken.
#asyncio.run(showGradioErrorAsync("Loading your prompts failed. It seems to be invalid. Your prompt table has been preserved.",5))
return [gr.Textbox.update(), gr.DataFrame.update(), gr.Textbox.update(),gr.Textbox.update()]
def clearPrompts():
return [
gr.DataFrame.update(value=[[0, "Infinite Zoom. Start over"]]),
gr.Textbox.update(""),
gr.Textbox.update(""),
gr.Textbox.update("")
]

67
iz_helpers/prompt_util.py Normal file
View File

@ -0,0 +1,67 @@
import json
from jsonschema import validate
from .static_variables import (
empty_prompt,
invalid_prompt,
jsonprompt_schemafile,
promptTableHeaders
)
def completeOptionals(j):
if isinstance(j, dict):
# Remove header information, user dont pimp our ui
if "prompts" in j:
if "headers" in j["prompts"]:
del j["prompts"]["headers"]
j["prompts"]["headers"]=promptTableHeaders
if "negPrompt" not in j:
j["negPrompt"]=""
if "prePrompt" not in j:
if "commonPromptPrefix" in j:
j["prePrompt"]=j["commonPromptPrefix"]
else:
j["prePrompt"]=""
if "postPrompt" not in j:
if "commonPromptSuffix" in j:
j["postPrompt"]=j["commonPromptSuffix"]
else:
j["postPrompt"]=""
return j
def validatePromptJson_throws(data):
with open(jsonprompt_schemafile, "r") as s:
schema = json.load(s)
try:
validate(instance=data, schema=schema)
except Exception:
raise Exception("Your prompts are not schema valid.")
return completeOptionals(data)
def readJsonPrompt(txt, returnFailPrompt=False):
if not txt:
return empty_prompt
try:
jpr = json.loads(txt)
except Exception:
if returnFailPrompt:
print (f"Infinite Zoom: Corrupted Json structure: {txt[:24]} ...")
return invalid_prompt
raise (f"Infinite Zoom: Corrupted Json structure: {txt[:24]} ...")
try:
return validatePromptJson_throws(jpr)
except Exception:
if returnFailPrompt:
return invalid_prompt
pass

View File

@ -0,0 +1,60 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "1.1",
"type": "object",
"properties": {
"prompts": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "array",
"items": [
{
"oneOf": [
{
"type": "integer",
"minimum": 0
},
{
"type": "string"
}
]
},
{
"type": "string"
}
],
"minItems": 0,
"maxItems": 999,
"uniqueItems": false
},
"minItems": 0
},
"headers": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 2
}
},
"required": [
"data"
]
},
"negPrompt": {
"type": "string"
},
"prePrompt": {
"type": "string"
},
"postPrompt": {
"type": "string"
}
},
"required": [
"prompts"
]
}

512
iz_helpers/run.py Normal file
View File

@ -0,0 +1,512 @@
import math, time, os
import numpy as np
from PIL import Image, ImageFilter, ImageDraw
from modules.ui import plaintext_to_html
import modules.shared as shared
from modules.paths_internal import script_path
from .helpers import (
fix_env_Path_ffprobe,
closest_upper_divisible_by_eight,
load_model_from_setting,
do_upscaleImg,
)
from .sd_helpers import renderImg2Img, renderTxt2Img
from .image import shrink_and_paste_on_blank
from .video import write_video
def crop_fethear_ellipse(image, feather_margin=30, width_offset=0, height_offset=0):
# Create a blank mask image with the same size as the original image
mask = Image.new("L", image.size, 0)
draw = ImageDraw.Draw(mask)
# Calculate the ellipse's bounding box
ellipse_box = (
width_offset,
height_offset,
image.width - width_offset,
image.height - height_offset,
)
# Draw the ellipse on the mask
draw.ellipse(ellipse_box, fill=255)
# Apply the mask to the original image
result = Image.new("RGBA", image.size)
result.paste(image, mask=mask)
# Crop the resulting image to the ellipse's bounding box
cropped_image = result.crop(ellipse_box)
# Create a new mask image with a black background (0)
mask = Image.new("L", cropped_image.size, 0)
draw = ImageDraw.Draw(mask)
# Draw an ellipse on the mask image
draw.ellipse(
(
0 + feather_margin,
0 + feather_margin,
cropped_image.width - feather_margin,
cropped_image.height - feather_margin,
),
fill=255,
outline=0,
)
# Apply a Gaussian blur to the mask image
mask = mask.filter(ImageFilter.GaussianBlur(radius=feather_margin / 2))
cropped_image.putalpha(mask)
res = Image.new(cropped_image.mode, (image.width, image.height))
paste_pos = (
int((res.width - cropped_image.width) / 2),
int((res.height - cropped_image.height) / 2),
)
res.paste(cropped_image, paste_pos)
return res
def outpaint_steps(
width,
height,
common_prompt_pre,
common_prompt_suf,
prompts,
negative_prompt,
seed,
sampler,
num_inference_steps,
guidance_scale,
inpainting_denoising_strength,
inpainting_mask_blur,
inpainting_fill_mode,
inpainting_full_res,
inpainting_padding,
init_img,
outpaint_steps,
out_config,
mask_width,
mask_height,
custom_exit_image,
frame_correction=True, # TODO: add frame_Correction in UI
):
main_frames = [init_img.convert("RGB")]
for i in range(outpaint_steps):
print_out = (
"Outpaint step: "
+ str(i + 1)
+ " / "
+ str(outpaint_steps)
+ " Seed: "
+ str(seed)
)
print(print_out)
current_image = main_frames[-1]
current_image = shrink_and_paste_on_blank(
current_image, mask_width, mask_height
)
mask_image = np.array(current_image)[:, :, 3]
mask_image = Image.fromarray(255 - mask_image).convert("RGB")
# create mask (black image with white mask_width width edges)
if custom_exit_image and ((i + 1) == outpaint_steps):
current_image = custom_exit_image.resize(
(width, height), resample=Image.LANCZOS
)
main_frames.append(current_image.convert("RGB"))
# print("using Custom Exit Image")
save2Collect(current_image, out_config, f"exit_img.png")
else:
pr = prompts[max(k for k in prompts.keys() if k <= i)]
processed, newseed = renderImg2Img(
f"{common_prompt_pre}\n{pr}\n{common_prompt_suf}".strip(),
negative_prompt,
sampler,
num_inference_steps,
guidance_scale,
seed,
width,
height,
current_image,
mask_image,
inpainting_denoising_strength,
inpainting_mask_blur,
inpainting_fill_mode,
inpainting_full_res,
inpainting_padding,
)
if len(processed.images) > 0:
main_frames.append(processed.images[0].convert("RGB"))
save2Collect(processed.images[0], out_config, f"outpain_step_{i}.png")
seed = newseed
# TODO: seed behavior
if frame_correction and inpainting_mask_blur > 0:
corrected_frame = crop_inner_image(
main_frames[i + 1], mask_width, mask_height
)
enhanced_img = crop_fethear_ellipse(
main_frames[i],
30,
inpainting_mask_blur / 3 // 2,
inpainting_mask_blur / 3 // 2,
)
save2Collect(main_frames[i], out_config, f"main_frame_{i}")
save2Collect(enhanced_img, out_config, f"main_frame_enhanced_{i}")
corrected_frame.paste(enhanced_img, mask=enhanced_img)
main_frames[i] = corrected_frame
# else :TEST
# current_image.paste(prev_image, mask=prev_image)
return main_frames, processed
def create_zoom(
common_prompt_pre,
prompts_array,
common_prompt_suf,
negative_prompt,
num_outpainting_steps,
guidance_scale,
num_inference_steps,
custom_init_image,
custom_exit_image,
video_frame_rate,
video_zoom_mode,
video_start_frame_dupe_amount,
video_last_frame_dupe_amount,
inpainting_mask_blur,
inpainting_fill_mode,
zoom_speed,
seed,
outputsizeW,
outputsizeH,
batchcount,
sampler,
upscale_do,
upscaler_name,
upscale_by,
inpainting_denoising_strength=1,
inpainting_full_res=0,
inpainting_padding=0,
progress=None,
):
for i in range(batchcount):
print(f"Batch {i+1}/{batchcount}")
result = create_zoom_single(
common_prompt_pre,
prompts_array,
common_prompt_suf,
negative_prompt,
num_outpainting_steps,
guidance_scale,
num_inference_steps,
custom_init_image,
custom_exit_image,
video_frame_rate,
video_zoom_mode,
video_start_frame_dupe_amount,
video_last_frame_dupe_amount,
inpainting_mask_blur,
inpainting_fill_mode,
zoom_speed,
seed,
outputsizeW,
outputsizeH,
sampler,
upscale_do,
upscaler_name,
upscale_by,
inpainting_denoising_strength,
inpainting_full_res,
inpainting_padding,
progress,
)
return result
def prepare_output_path():
isCollect = shared.opts.data.get("infzoom_collectAllResources", False)
output_path = shared.opts.data.get("infzoom_outpath", "outputs")
save_path = os.path.join(
output_path, shared.opts.data.get("infzoom_outSUBpath", "infinite-zooms")
)
if isCollect:
save_path = os.path.join(save_path, "iz_collect" + str(int(time.time())))
if not os.path.exists(save_path):
os.makedirs(save_path)
video_filename = os.path.join(
save_path, "infinite_zoom_" + str(int(time.time())) + ".mp4"
)
return {
"isCollect": isCollect,
"save_path": save_path,
"video_filename": video_filename,
}
def save2Collect(img, out_config, name):
if out_config["isCollect"]:
img.save(f'{out_config["save_path"]}/{name}.png')
def frame2Collect(all_frames, out_config):
save2Collect(all_frames[-1], out_config, f"frame_{len(all_frames)}")
def frames2Collect(all_frames, out_config):
for i, f in enumerate(all_frames):
save2Collect(f, out_config, f"frame_{i}")
def crop_inner_image(outpainted_img, width_offset, height_offset):
width, height = outpainted_img.size
center_x, center_y = int(width / 2), int(height / 2)
# Crop the image to the center
cropped_img = outpainted_img.crop(
(
center_x - width_offset,
center_y - height_offset,
center_x + width_offset,
center_y + height_offset,
)
)
prev_step_img = cropped_img.resize((width, height), resample=Image.LANCZOS)
# resized_img = resized_img.filter(ImageFilter.SHARPEN)
return prev_step_img
def create_zoom_single(
common_prompt_pre,
prompts_array,
common_prompt_suf,
negative_prompt,
num_outpainting_steps,
guidance_scale,
num_inference_steps,
custom_init_image,
custom_exit_image,
video_frame_rate,
video_zoom_mode,
video_start_frame_dupe_amount,
video_last_frame_dupe_amount,
inpainting_mask_blur,
inpainting_fill_mode,
zoom_speed,
seed,
outputsizeW,
outputsizeH,
sampler,
upscale_do,
upscaler_name,
upscale_by,
inpainting_denoising_strength,
inpainting_full_res,
inpainting_padding,
progress,
):
# try:
# if gr.Progress() is not None:
# progress = gr.Progress()
# progress(0, desc="Preparing Initial Image")
# except Exception:
# pass
fix_env_Path_ffprobe()
out_config = prepare_output_path()
prompts = {}
for x in prompts_array:
try:
key = int(x[0])
value = str(x[1])
prompts[key] = value
except ValueError:
pass
assert len(prompts_array) > 0, "prompts is empty"
width = closest_upper_divisible_by_eight(outputsizeW)
height = closest_upper_divisible_by_eight(outputsizeH)
current_image = Image.new(mode="RGBA", size=(width, height))
mask_image = np.array(current_image)[:, :, 3]
mask_image = Image.fromarray(255 - mask_image).convert("RGB")
current_image = current_image.convert("RGB")
current_seed = seed
if custom_init_image:
current_image = custom_init_image.resize(
(width, height), resample=Image.LANCZOS
)
save2Collect(current_image, out_config, f"init_custom.png")
else:
load_model_from_setting(
"infzoom_txt2img_model", progress, "Loading Model for txt2img: "
)
pr = prompts[min(k for k in prompts.keys() if k >= 0)]
processed, newseed = renderTxt2Img(
f"{common_prompt_pre}\n{pr}\n{common_prompt_suf}".strip(),
negative_prompt,
sampler,
num_inference_steps,
guidance_scale,
current_seed,
width,
height,
)
if len(processed.images) > 0:
current_image = processed.images[0]
save2Collect(current_image, out_config, f"init_txt2img.png")
current_seed = newseed
mask_width = math.trunc(width / 4) # was initially 512px => 128px
mask_height = math.trunc(height / 4) # was initially 512px => 128px
num_interpol_frames = round(video_frame_rate * zoom_speed)
all_frames = []
if upscale_do and progress:
progress(0, desc="upscaling inital image")
load_model_from_setting(
"infzoom_inpainting_model", progress, "Loading Model for inpainting/img2img: "
)
main_frames, processed = outpaint_steps(
width,
height,
common_prompt_pre,
common_prompt_suf,
prompts,
negative_prompt,
seed,
sampler,
num_inference_steps,
guidance_scale,
inpainting_denoising_strength,
inpainting_mask_blur,
inpainting_fill_mode,
inpainting_full_res,
inpainting_padding,
current_image,
num_outpainting_steps,
out_config,
mask_width,
mask_height,
custom_exit_image,
)
all_frames.append(
do_upscaleImg(main_frames[0], upscale_do, upscaler_name, upscale_by)
if upscale_do
else main_frames[0]
)
for i in range(len(main_frames) - 1):
# interpolation steps between 2 inpainted images (=sequential zoom and crop)
for j in range(num_interpol_frames - 1):
current_image = main_frames[i + 1]
interpol_image = current_image
save2Collect(interpol_image, out_config, f"interpol_img_{i}_{j}].png")
interpol_width = math.ceil(
(
1
- (1 - 2 * mask_width / width)
** (1 - (j + 1) / num_interpol_frames)
)
* width
/ 2
)
interpol_height = math.ceil(
(
1
- (1 - 2 * mask_height / height)
** (1 - (j + 1) / num_interpol_frames)
)
* height
/ 2
)
interpol_image = interpol_image.crop(
(
interpol_width,
interpol_height,
width - interpol_width,
height - interpol_height,
)
)
interpol_image = interpol_image.resize((width, height))
save2Collect(interpol_image, out_config, f"interpol_resize_{i}_{j}.png")
# paste the higher resolution previous image in the middle to avoid drop in quality caused by zooming
interpol_width2 = math.ceil(
(1 - (width - 2 * mask_width) / (width - 2 * interpol_width))
/ 2
* width
)
interpol_height2 = math.ceil(
(1 - (height - 2 * mask_height) / (height - 2 * interpol_height))
/ 2
* height
)
prev_image_fix_crop = shrink_and_paste_on_blank(
main_frames[i], interpol_width2, interpol_height2
)
interpol_image.paste(prev_image_fix_crop, mask=prev_image_fix_crop)
save2Collect(interpol_image, out_config, f"interpol_prevcrop_{i}_{j}.png")
if upscale_do and progress:
progress(((i + 1) / num_outpainting_steps), desc="upscaling interpol")
all_frames.append(
do_upscaleImg(interpol_image, upscale_do, upscaler_name, upscale_by)
if upscale_do
else interpol_image
)
if upscale_do and progress:
progress(((i + 1) / num_outpainting_steps), desc="upscaling current")
all_frames.append(
do_upscaleImg(current_image, upscale_do, upscaler_name, upscale_by)
if upscale_do
else current_image
)
frames2Collect(all_frames, out_config)
write_video(
out_config["video_filename"],
all_frames,
video_frame_rate,
video_zoom_mode,
int(video_start_frame_dupe_amount),
int(video_last_frame_dupe_amount),
)
print("Video saved in: " + os.path.join(script_path, out_config["video_filename"]))
return (
out_config["video_filename"],
main_frames,
processed.js(),
plaintext_to_html(processed.info),
plaintext_to_html(""),
)

81
iz_helpers/sd_helpers.py Normal file
View File

@ -0,0 +1,81 @@
from modules.processing import (
process_images,
StableDiffusionProcessingTxt2Img,
StableDiffusionProcessingImg2Img,
)
import modules.shared as shared
def renderTxt2Img(
prompt, negative_prompt, sampler, steps, cfg_scale, seed, width, height
):
processed = None
p = StableDiffusionProcessingTxt2Img(
sd_model=shared.sd_model,
outpath_samples=shared.opts.outdir_txt2img_samples,
outpath_grids=shared.opts.outdir_txt2img_grids,
prompt=prompt,
negative_prompt=negative_prompt,
seed=seed,
sampler_name=sampler,
n_iter=1,
steps=steps,
cfg_scale=cfg_scale,
width=width,
height=height,
)
processed = process_images(p)
newseed = p.seed
return processed, newseed
def renderImg2Img(
prompt,
negative_prompt,
sampler,
steps,
cfg_scale,
seed,
width,
height,
init_image,
mask_image,
inpainting_denoising_strength,
inpainting_mask_blur,
inpainting_fill_mode,
inpainting_full_res,
inpainting_padding,
):
processed = None
p = StableDiffusionProcessingImg2Img(
sd_model=shared.sd_model,
outpath_samples=shared.opts.outdir_img2img_samples,
outpath_grids=shared.opts.outdir_img2img_grids,
prompt=prompt,
negative_prompt=negative_prompt,
seed=seed,
sampler_name=sampler,
n_iter=1,
steps=steps,
cfg_scale=cfg_scale,
width=width,
height=height,
init_images=[init_image],
denoising_strength=inpainting_denoising_strength,
mask_blur=inpainting_mask_blur,
inpainting_fill=inpainting_fill_mode,
inpaint_full_res=inpainting_full_res,
inpaint_full_res_padding=inpainting_padding,
mask=mask_image,
)
# p.latent_mask = Image.new("RGB", (p.width, p.height), "white")
processed = process_images(p)
# For those that use Image grids this will make sure that ffmpeg does not crash out
if (len(processed.images) > 1) and (processed.images[0].size[0] != processed.images[-1].size[0]):
processed.images.pop(0)
print("\nGrid image detected applying patch")
newseed = p.seed
return processed, newseed

108
iz_helpers/settings.py Normal file
View File

@ -0,0 +1,108 @@
import gradio as gr
import modules.shared as shared
from .static_variables import default_prompt
def on_ui_settings():
section = ("infinite-zoom", "Infinite Zoom")
shared.opts.add_option(
"infzoom_outpath",
shared.OptionInfo(
"outputs",
"Path where to store your infinite video. Default is Outputs",
gr.Textbox,
{"interactive": True},
section=section,
),
)
shared.opts.add_option(
"infzoom_outSUBpath",
shared.OptionInfo(
"infinite-zooms",
"Which subfolder name to be created in the outpath. Default is 'infinite-zooms'",
gr.Textbox,
{"interactive": True},
section=section,
),
)
shared.opts.add_option(
"infzoom_outsizeW",
shared.OptionInfo(
512,
"Default width of your video",
gr.Slider,
{"minimum": 16, "maximum": 2048, "step": 16},
section=section,
),
)
shared.opts.add_option(
"infzoom_outsizeH",
shared.OptionInfo(
512,
"Default height your video",
gr.Slider,
{"minimum": 16, "maximum": 2048, "step": 16},
section=section,
),
)
shared.opts.add_option(
"infzoom_ffprobepath",
shared.OptionInfo(
"",
"Writing videos has dependency to an existing FFPROBE executable on your machine. D/L here (https://github.com/BtbN/FFmpeg-Builds/releases) your OS variant and point to your installation path",
gr.Textbox,
{"interactive": True},
section=section,
),
)
shared.opts.add_option(
"infzoom_txt2img_model",
shared.OptionInfo(
None,
"Name of your desired model to render keyframes (txt2img)",
gr.Dropdown,
lambda: {"choices": [x for x in list(shared.list_checkpoint_tiles()) if "inpainting" not in x]},
section=section,
),
)
shared.opts.add_option(
"infzoom_inpainting_model",
shared.OptionInfo(
None,
"Name of your desired inpaint model (img2img-inpaint). Default is vanilla sd-v1-5-inpainting.ckpt ",
gr.Dropdown,
lambda: {"choices": [x for x in list(shared.list_checkpoint_tiles()) if "inpainting" in x]},
section=section,
),
)
shared.opts.add_option(
"infzoom_defPrompt",
shared.OptionInfo(
default_prompt,
"Default prompt-setup to start with'",
gr.Code,
{"interactive": True, "language": "json"},
section=section,
),
)
shared.opts.add_option(
"infzoom_collectAllResources",
shared.OptionInfo(
False,
"!!! Store all images (txt2img, init_image,exit_image, inpainting, interpolation) into one folder in your OUTPUT Path. Very slow, a lot of data. Dont do this on long runs !!!",
gr.Checkbox,
{"interactive": True},
section=section,
),
)

View File

@ -0,0 +1,51 @@
import os
from modules import scripts
import modules.sd_samplers
default_sampling_steps = 35
default_sampler = "DDIM"
default_cfg_scale = 8
default_mask_blur = 48
default_total_outpaints = 5
promptTableHeaders = ["Start at second [0,1,...]", "Prompt"]
default_prompt = """
{
"prePrompt": "Huge spectacular Waterfall in ",
"prompts": {
"data": [
[0, "a dense tropical forest"],
[2, "a Lush jungle"],
[3, "a Thick rainforest"],
[5, "a Verdant canopy"]
]
},
"postPrompt": "epic perspective,(vegetation overgrowth:1.3)(intricate, ornamentation:1.1),(baroque:1.1), fantasy, (realistic:1) digital painting , (magical,mystical:1.2) , (wide angle shot:1.4), (landscape composed:1.2)(medieval:1.1),(tropical forest:1.4),(river:1.3) volumetric lighting ,epic, style by Alex Horley Wenjun Lin greg rutkowski Ruan Jia (Wayne Barlowe:1.2)",
"negPrompt": "frames, border, edges, borderline, text, character, duplicate, error, out of frame, watermark, low quality, ugly, deformed, blur, bad-artist"
}
"""
empty_prompt = '{"prompts":{"data":[],"negPrompt":"", prePrompt:"", postPrompt:""}'
invalid_prompt = {
"prompts": {
"data": [[0, "Your prompt-json is invalid, please check Settings"]],
},
"negPrompt": "Invalid prompt-json",
"prePrompt": "Invalid prompt",
"postPrompt": "Invalid prompt",
}
available_samplers = [
s.name for s in modules.sd_samplers.samplers if "UniPc" not in s.name
]
current_script_dir = scripts.basedir().split(os.sep)[
-2:
] # contains install and our extension foldername
jsonprompt_schemafile = (
current_script_dir[0]
+ "/"
+ current_script_dir[1]
+ "/iz_helpers/promptschema.json"
)

315
iz_helpers/ui.py Normal file
View File

@ -0,0 +1,315 @@
import gradio as gr
from .run import create_zoom
import modules.shared as shared
from modules.call_queue import wrap_gradio_gpu_call
from modules.ui import create_output_panel
from .static_variables import (
default_prompt,
available_samplers,
default_total_outpaints,
default_sampling_steps,
default_cfg_scale,
default_mask_blur,
default_sampler,
)
from .helpers import putPrompts, clearPrompts
from .prompt_util import readJsonPrompt
from .static_variables import promptTableHeaders
def on_ui_tabs():
with gr.Blocks(analytics_enabled=False) as infinite_zoom_interface:
gr.HTML(
"""
<p style="text-align: center;">
<a target="_blank" href="https://github.com/v8hid/infinite-zoom-automatic1111-webui"><img src="https://img.shields.io/static/v1?label=github&message=repository&color=blue&style=flat&logo=github&logoColor=white" style="display: inline;" alt="GitHub Repo"/></a>
<a href="https://discord.gg/v2nHqSrWdW"><img src="https://img.shields.io/discord/1095469311830806630?color=blue&label=discord&logo=discord&logoColor=white" style="display: inline;" alt="Discord server"></a>
</p>
"""
)
with gr.Row():
generate_btn = gr.Button(value="Generate video", variant="primary")
interrupt = gr.Button(value="Interrupt", elem_id="interrupt_training")
with gr.Row():
with gr.Column(scale=1, variant="panel"):
with gr.Tab("Main"):
with gr.Row():
batchcount_slider = gr.Slider(
minimum=1,
maximum=25,
value=shared.opts.data.get("infzoom_batchcount", 1),
step=1,
label="Batch Count",
)
main_outpaint_steps = gr.Number(
label="Total video length [s]",
value=default_total_outpaints,
precision=0,
interactive=True,
)
# safe reading json prompt
pr = shared.opts.data.get("infzoom_defPrompt", default_prompt)
jpr = readJsonPrompt(pr, True)
main_common_prompt_pre = gr.Textbox(
value=jpr["prePrompt"], label="Common Prompt Prefix"
)
main_prompts = gr.Dataframe(
type="array",
headers=promptTableHeaders,
datatype=["number", "str"],
row_count=1,
col_count=(2, "fixed"),
value=jpr["prompts"],
wrap=True,
)
main_common_prompt_suf = gr.Textbox(
value=jpr["postPrompt"], label="Common Prompt Suffix"
)
main_negative_prompt = gr.Textbox(
value=jpr["negPrompt"], label="Negative Prompt"
)
# these button will be moved using JS under the dataframe view as small ones
exportPrompts_button = gr.Button(
value="Export prompts",
variant="secondary",
elem_classes="sm infzoom_tab_butt",
elem_id="infzoom_exP_butt",
)
importPrompts_button = gr.UploadButton(
label="Import prompts",
variant="secondary",
elem_classes="sm infzoom_tab_butt",
elem_id="infzoom_imP_butt",
)
exportPrompts_button.click(
None,
_js="exportPrompts",
inputs=[
main_common_prompt_pre,
main_prompts,
main_common_prompt_suf,
main_negative_prompt,
],
outputs=None,
)
importPrompts_button.upload(
fn=putPrompts,
outputs=[
main_common_prompt_pre,
main_prompts,
main_common_prompt_suf,
main_negative_prompt,
],
inputs=[importPrompts_button],
)
clearPrompts_button = gr.Button(
value="Clear prompts",
variant="secondary",
elem_classes="sm infzoom_tab_butt",
elem_id="infzoom_clP_butt",
)
clearPrompts_button.click(
fn=clearPrompts,
inputs=[],
outputs=[
main_prompts,
main_negative_prompt,
main_common_prompt_pre,
main_common_prompt_suf,
],
)
with gr.Accordion("Render settings"):
with gr.Row():
seed = gr.Number(
label="Seed", value=-1, precision=0, interactive=True
)
main_sampler = gr.Dropdown(
label="Sampler",
choices=available_samplers,
value=default_sampler,
type="value",
)
with gr.Row():
main_width = gr.Slider(
minimum=16,
maximum=2048,
value=shared.opts.data.get("infzoom_outsizeW", 512),
step=16,
label="Output Width",
)
main_height = gr.Slider(
minimum=16,
maximum=2048,
value=shared.opts.data.get("infzoom_outsizeH", 512),
step=16,
label="Output Height",
)
with gr.Row():
main_guidance_scale = gr.Slider(
minimum=0.1,
maximum=15,
step=0.1,
value=default_cfg_scale,
label="Guidance Scale",
)
sampling_step = gr.Slider(
minimum=1,
maximum=150,
step=1,
value=default_sampling_steps,
label="Sampling Steps for each outpaint",
)
with gr.Row():
init_image = gr.Image(
type="pil", label="Custom initial image"
)
exit_image = gr.Image(
type="pil", label="Custom exit image", visible=False
)
with gr.Tab("Video"):
video_frame_rate = gr.Slider(
label="Frames per second",
value=30,
minimum=1,
maximum=60,
)
video_zoom_mode = gr.Radio(
label="Zoom mode",
choices=["Zoom-out", "Zoom-in"],
value="Zoom-out",
type="index",
)
video_start_frame_dupe_amount = gr.Slider(
label="number of start frame dupe",
info="Frames to freeze at the start of the video",
value=0,
minimum=1,
maximum=60,
)
video_last_frame_dupe_amount = gr.Slider(
label="number of last frame dupe",
info="Frames to freeze at the end of the video",
value=0,
minimum=1,
maximum=60,
)
video_zoom_speed = gr.Slider(
label="Zoom Speed",
value=1.0,
minimum=0.1,
maximum=20.0,
step=0.1,
info="Zoom speed in seconds (higher values create slower zoom)",
)
with gr.Tab("Outpaint"):
inpainting_mask_blur = gr.Slider(
label="Mask Blur",
minimum=0,
maximum=64,
value=default_mask_blur,
)
inpainting_fill_mode = gr.Radio(
label="Masked content",
choices=["fill", "original", "latent noise", "latent nothing"],
value="latent noise",
type="index",
)
with gr.Tab("Post proccess"):
upscale_do = gr.Checkbox(False, label="Enable Upscale")
upscaler_name = gr.Dropdown(
label="Upscaler",
elem_id="infZ_upscaler",
choices=[x.name for x in shared.sd_upscalers],
value=shared.sd_upscalers[0].name,
)
upscale_by = gr.Slider(
label="Upscale by factor",
minimum=1,
maximum=8,
step=0.5,
value=2,
)
with gr.Accordion("Help", open=False):
gr.Markdown(
"""# Performance critical
Depending on amount of frames and which upscaler you choose it might took a long time to render.
Our best experience and trade-off is the R-ERSGAn4x upscaler.
"""
)
with gr.Column(scale=1, variant="compact"):
output_video = gr.Video(label="Output", width=512, height=512)
output_panel = create_output_panel(
"infinite-zoom", shared.opts.outdir_img2img_samples
)
if isinstance(output_panel, tuple):
(
out_image,
generation_info,
html_info,
html_log,
) = output_panel
else:
out_image = output_panel.gallery
generation_info = output_panel.generation_info
html_info = output_panel.infotext
html_log = output_panel.html_log
generate_btn.click(
fn=wrap_gradio_gpu_call(create_zoom, extra_outputs=[None, "", ""]),
inputs=[
main_common_prompt_pre,
main_prompts,
main_common_prompt_suf,
main_negative_prompt,
main_outpaint_steps,
main_guidance_scale,
sampling_step,
init_image,
exit_image,
video_frame_rate,
video_zoom_mode,
video_start_frame_dupe_amount,
video_last_frame_dupe_amount,
inpainting_mask_blur,
inpainting_fill_mode,
video_zoom_speed,
seed,
main_width,
main_height,
batchcount_slider,
main_sampler,
upscale_do,
upscaler_name,
upscale_by,
],
outputs=[output_video, out_image, generation_info, html_info, html_log],
)
main_prompts.change(
fn=checkPrompts, inputs=[main_prompts], outputs=[generate_btn]
)
interrupt.click(fn=lambda: shared.state.interrupt(), inputs=[], outputs=[])
infinite_zoom_interface.queue()
return [(infinite_zoom_interface, "Infinite Zoom", "iz_interface")]
def checkPrompts(p):
return gr.Button.update(
interactive=any(0 in sublist for sublist in p)
or any("0" in sublist for sublist in p)
)

View File

@ -13,9 +13,8 @@ def write_video(file_path, frames, fps, reversed=True, start_frame_dupe_amount=1
if reversed == True:
frames = frames[::-1]
# Get dimensions of the first frames, all subsequent has to be same sized
for k in frames:
assert (k.size == frames[0].size,"Different frame sizes found!")
# Drop missformed frames
frames = [frame for frame in frames if frame.size == frames[0].size]
# Create an imageio video writer, avoid block size of 512.
writer = imageio.get_writer(file_path, fps=fps, macro_block_size=None)

View File

@ -0,0 +1,49 @@
// mouseover tooltips for various UI elements
infzoom_titles = {
"Batch Count":"How many separate videos to create",
"Total video length [s]":"For each seconds frame (FPS) will be generated. Define prompts at which time they should start wihtin this duration.",
"Common Prompt Prefix":"Prompt inserted before each step",
"Common Prompt Suffix":"Prompt inserted after each step",
"Negative Prompt":"What your model shall avoid",
"Export prompts": "Downloads a JSON file to save all prompts",
"Import prompts": "Restore Prompts table from a specific JSON file",
"Clear prompts": "Start over, remove all entries from prompt table, prefix, suffix, negative",
"Custom initial image":"An image at the end resp. begin of your movie, depending or ZoomIn or Out",
"Custom exit image":"An image at the end resp. begin of your movie, depending or ZoomIn or Out",
"Zoom Speed":"Varies additional frames per second",
"Start at second [0,1,...]": "At which time the prompt has to be occure. We need at least one prompt starting at time 0",
"Generate video": "Start rendering. If it´s disabled the prompt table is invalid, check we have a start prompt at time 0"
}
onUiUpdate(function(){
gradioApp().querySelectorAll('span, button, select, p').forEach(function(span){
tooltip = infzoom_titles[span.textContent];
if(!tooltip){
tooltip = infzoom_titles[span.value];
}
if(!tooltip){
for (const c of span.classList) {
if (c in infzoom_titles) {
tooltip = infzoom_titles[c];
break;
}
}
}
if(tooltip){
span.title = tooltip;
}
})
gradioApp().querySelectorAll('select').forEach(function(select){
if (select.onchange != null) return;
select.onchange = function(){
select.title = infzoom_titles[select.value] || "";
}
})
})

View File

@ -1,9 +1,9 @@
// Function to download data to a file
function exportPrompts(p, np, filename = "infinite-zoom-prompts.json") {
function exportPrompts(cppre,p, cpsuf,np, filename = "infinite-zoom-prompts.json") {
let J = { prompts: p, negPrompt: np }
let J = { prompts: p, negPrompt: np, prePrompt: cppre, postPrompt: cpsuf }
var file = new Blob([JSON.stringify(J)], { type: "text/csv" });
var file = new Blob([JSON.stringify(J,null,2)], { type: "text/csv" });
if (window.navigator.msSaveOrOpenBlob) // IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
else { // Others

View File

@ -1,880 +1,5 @@
import sys
import os
import time
import json
from jsonschema import validate
import numpy as np
import gradio as gr
from PIL import Image
import math
import json
from iz_helpers import shrink_and_paste_on_blank, write_video
from webui import wrap_gradio_gpu_call
from modules import script_callbacks, scripts
import modules.shared as shared
from modules.processing import (
process_images,
StableDiffusionProcessingTxt2Img,
StableDiffusionProcessingImg2Img,
)
from scripts import postprocessing_upscale
from modules.ui import create_output_panel, plaintext_to_html
import modules.sd_models
import modules.sd_samplers
from modules import scripts
usefulDirs = scripts.basedir().split(os.sep)[
-2:
] # contains install and our extension foldername
jsonprompt_schemafile = (
usefulDirs[0] + "/" + usefulDirs[1] + "/scripts/promptschema.json"
)
available_samplers = [s.name for s in modules.sd_samplers.samplers if "UniPc" not in s.name]
default_prompt = """
{
"prompts":{
"headers":["outpaint steps","prompt"],
"data":[
[0,"Huge spectacular Waterfall in a dense tropical forest,epic perspective,(vegetation overgrowth:1.3)(intricate, ornamentation:1.1),(baroque:1.1), fantasy, (realistic:1) digital painting , (magical,mystical:1.2) , (wide angle shot:1.4), (landscape composed:1.2)(medieval:1.1), divine,cinematic,(tropical forest:1.4),(river:1.3)mythology,india, volumetric lighting, Hindu ,epic, Alex Horley Wenjun Lin greg rutkowski Ruan Jia (Wayne Barlowe:1.2) <lora:epiNoiseoffset_v2:0.6> "],
]
},
"negPrompt":"frames, borderline, text, character, duplicate, error, out of frame, watermark, low quality, ugly, deformed, blur bad-artist"
}
"""
empty_prompt = (
'{"prompts":{"data":[],"headers":["outpaint steps","prompt"]},"negPrompt":""}'
)
# must be python dict
invalid_prompt = {
"prompts": {
"data": [[0, "Your prompt-json is invalid, please check Settings"]],
"headers": ["outpaint steps", "prompt"],
},
"negPrompt": "Invalid prompt-json",
}
def closest_upper_divisible_by_eight(num):
if num % 8 == 0:
return num
else:
return math.ceil(num / 8) * 8
# example fail: 720 px width * 1.66 upscale => 1195.2 => 1195 crash
# 512 px * 1.66 = 513.66 = ?
# assume ffmpeg will CUT to integer
# 721 /720
def do_upscaleImg(curImg, upscale_do, upscaler_name, upscale_by):
if not upscale_do:
return curImg
# ensure even width and even height for ffmpeg
# if odd, switch to scale to mode
rwidth = round(curImg.width * upscale_by)
rheight = round(curImg.height * upscale_by)
ups_mode = 2 # upscale_by
if ( (rwidth %2) == 1 ):
ups_mode = 1
rwidth += 1
if ( (rheight %2) == 1 ):
ups_mode = 1
rheight += 1
if (1 == ups_mode ):
print ("Infinite Zoom: aligning output size to even width and height: " + str(rwidth) +" x "+str(rheight), end='\r' )
pp = postprocessing_upscale.scripts_postprocessing.PostprocessedImage(
curImg
)
ups = postprocessing_upscale.ScriptPostprocessingUpscale()
ups.process(
pp,
upscale_mode=ups_mode,
upscale_by=upscale_by,
upscale_to_width=rwidth,
upscale_to_height=rheight,
upscale_crop=False,
upscaler_1_name=upscaler_name,
upscaler_2_name=None,
upscaler_2_visibility=0.0,
)
return pp.image
def renderTxt2Img(prompt, negative_prompt, sampler, steps, cfg_scale, width, height):
processed = None
p = StableDiffusionProcessingTxt2Img(
sd_model=shared.sd_model,
outpath_samples=shared.opts.outdir_txt2img_samples,
outpath_grids=shared.opts.outdir_txt2img_grids,
prompt=prompt,
negative_prompt=negative_prompt,
# seed=-1,
sampler_name=sampler,
n_iter=1,
steps=steps,
cfg_scale=cfg_scale,
width=width,
height=height,
)
processed = process_images(p)
return processed
def renderImg2Img(
prompt,
negative_prompt,
sampler,
steps,
cfg_scale,
width,
height,
init_image,
mask_image,
inpainting_denoising_strength,
inpainting_mask_blur,
inpainting_fill_mode,
inpainting_full_res,
inpainting_padding,
):
processed = None
p = StableDiffusionProcessingImg2Img(
sd_model=shared.sd_model,
outpath_samples=shared.opts.outdir_img2img_samples,
outpath_grids=shared.opts.outdir_img2img_grids,
prompt=prompt,
negative_prompt=negative_prompt,
# seed=-1,
sampler_name=sampler,
n_iter=1,
steps=steps,
cfg_scale=cfg_scale,
width=width,
height=height,
init_images=[init_image],
denoising_strength=inpainting_denoising_strength,
mask_blur=inpainting_mask_blur,
inpainting_fill=inpainting_fill_mode,
inpaint_full_res=inpainting_full_res,
inpaint_full_res_padding=inpainting_padding,
mask=mask_image,
)
# p.latent_mask = Image.new("RGB", (p.width, p.height), "white")
processed = process_images(p)
return processed
def fix_env_Path_ffprobe():
envpath = os.environ["PATH"]
ffppath = shared.opts.data.get("infzoom_ffprobepath", "")
if ffppath and not ffppath in envpath:
path_sep = ";" if os.name == "nt" else ":"
os.environ["PATH"] = envpath + path_sep + ffppath
def load_model_from_setting(model_field_name, progress, progress_desc):
# fix typo in Automatic1111 vs Vlad111
if hasattr(modules.sd_models, "checkpoint_alisases"):
checkPList = modules.sd_models.checkpoint_alisases
elif hasattr(modules.sd_models, "checkpoint_aliases"):
checkPList = modules.sd_models.checkpoint_aliases
else:
raise Exception("This is not a compatible StableDiffusion Platform, can not access checkpoints")
model_name = shared.opts.data.get(model_field_name)
if model_name is not None and model_name != "":
checkinfo = checkPList[model_name]
if not checkinfo:
raise NameError(model_field_name + " Does not exist in your models.")
if progress:
progress(0, desc=progress_desc + checkinfo.name)
modules.sd_models.load_model(checkinfo)
def create_zoom(
prompts_array,
negative_prompt,
num_outpainting_steps,
guidance_scale,
num_inference_steps,
custom_init_image,
custom_exit_image,
video_frame_rate,
video_zoom_mode,
video_start_frame_dupe_amount,
video_last_frame_dupe_amount,
inpainting_denoising_strength,
inpainting_mask_blur,
inpainting_fill_mode,
inpainting_full_res,
inpainting_padding,
zoom_speed,
outputsizeW,
outputsizeH,
batchcount,
sampler,
upscale_do,
upscaler_name,
upscale_by,
progress=None,
):
for i in range(batchcount):
print(f"Batch {i+1}/{batchcount}")
result = create_zoom_single(
prompts_array,
negative_prompt,
num_outpainting_steps,
guidance_scale,
num_inference_steps,
custom_init_image,
custom_exit_image,
video_frame_rate,
video_zoom_mode,
video_start_frame_dupe_amount,
video_last_frame_dupe_amount,
inpainting_denoising_strength,
inpainting_mask_blur,
inpainting_fill_mode,
inpainting_full_res,
inpainting_padding,
zoom_speed,
outputsizeW,
outputsizeH,
sampler,
upscale_do,
upscaler_name,
upscale_by,
progress,
)
return result
def create_zoom_single(
prompts_array,
negative_prompt,
num_outpainting_steps,
guidance_scale,
num_inference_steps,
custom_init_image,
custom_exit_image,
video_frame_rate,
video_zoom_mode,
video_start_frame_dupe_amount,
video_last_frame_dupe_amount,
inpainting_denoising_strength,
inpainting_mask_blur,
inpainting_fill_mode,
inpainting_full_res,
inpainting_padding,
zoom_speed,
outputsizeW,
outputsizeH,
sampler,
upscale_do,
upscaler_name,
upscale_by,
progress=None,
):
# try:
# if gr.Progress() is not None:
# progress = gr.Progress()
# progress(0, desc="Preparing Initial Image")
# except Exception:
# pass
fix_env_Path_ffprobe()
prompts = {}
for x in prompts_array:
try:
key = int(x[0])
value = str(x[1])
prompts[key] = value
except ValueError:
pass
assert len(prompts_array) > 0, "prompts is empty"
width = closest_upper_divisible_by_eight(outputsizeW)
height = closest_upper_divisible_by_eight(outputsizeH)
current_image = Image.new(mode="RGBA", size=(width, height))
mask_image = np.array(current_image)[:, :, 3]
mask_image = Image.fromarray(255 - mask_image).convert("RGB")
current_image = current_image.convert("RGB")
if custom_init_image:
current_image = custom_init_image.resize(
(width, height), resample=Image.LANCZOS
)
else:
load_model_from_setting("infzoom_txt2img_model", progress, "Loading Model for txt2img: ")
processed = renderTxt2Img(
prompts[min(k for k in prompts.keys() if k >= 0)],
negative_prompt,
sampler,
num_inference_steps,
guidance_scale,
width,
height,
)
current_image = processed.images[0]
mask_width = math.trunc(width / 4) # was initially 512px => 128px
mask_height = math.trunc(height / 4) # was initially 512px => 128px
num_interpol_frames = round(video_frame_rate * zoom_speed)
all_frames = []
if upscale_do and progress:
progress(0, desc="upscaling inital image")
all_frames.append(
do_upscaleImg(current_image, upscale_do, upscaler_name, upscale_by)
if upscale_do
else current_image
)
load_model_from_setting("infzoom_inpainting_model", progress, "Loading Model for inpainting/img2img: " )
for i in range(num_outpainting_steps):
print_out = "Outpaint step: " + str(i + 1) + " / " + str(num_outpainting_steps)
print(print_out)
if progress:
progress(((i + 1) / num_outpainting_steps), desc=print_out)
prev_image_fix = current_image
prev_image = shrink_and_paste_on_blank(current_image, mask_width, mask_height)
current_image = prev_image
# create mask (black image with white mask_width width edges)
mask_image = np.array(current_image)[:, :, 3]
mask_image = Image.fromarray(255 - mask_image).convert("RGB")
# inpainting step
current_image = current_image.convert("RGB")
processed = renderImg2Img(
prompts[max(k for k in prompts.keys() if k <= i)],
negative_prompt,
sampler,
num_inference_steps,
guidance_scale,
width,
height,
current_image,
mask_image,
inpainting_denoising_strength,
inpainting_mask_blur,
inpainting_fill_mode,
inpainting_full_res,
inpainting_padding,
)
current_image = processed.images[0]
current_image.paste(prev_image, mask=prev_image)
# interpolation steps between 2 inpainted images (=sequential zoom and crop)
for j in range(num_interpol_frames - 1):
interpol_image = current_image
interpol_width = round(
(
1
- (1 - 2 * mask_width / width)
** (1 - (j + 1) / num_interpol_frames)
)
* width
/ 2
)
interpol_height = round(
(
1
- (1 - 2 * mask_height / height)
** (1 - (j + 1) / num_interpol_frames)
)
* height
/ 2
)
interpol_image = interpol_image.crop(
(
interpol_width,
interpol_height,
width - interpol_width,
height - interpol_height,
)
)
interpol_image = interpol_image.resize((width, height))
# paste the higher resolution previous image in the middle to avoid drop in quality caused by zooming
interpol_width2 = round(
(1 - (width - 2 * mask_width) / (width - 2 * interpol_width))
/ 2
* width
)
interpol_height2 = round(
(1 - (height - 2 * mask_height) / (height - 2 * interpol_height))
/ 2
* height
)
prev_image_fix_crop = shrink_and_paste_on_blank(
prev_image_fix, interpol_width2, interpol_height2
)
interpol_image.paste(prev_image_fix_crop, mask=prev_image_fix_crop)
if upscale_do and progress:
progress(((i + 1) / num_outpainting_steps), desc="upscaling interpol")
all_frames.append(
do_upscaleImg(interpol_image, upscale_do, upscaler_name, upscale_by)
if upscale_do
else interpol_image
)
if upscale_do and progress:
progress(((i + 1) / num_outpainting_steps), desc="upscaling current")
all_frames.append(
do_upscaleImg(current_image, upscale_do, upscaler_name, upscale_by)
if upscale_do
else current_image
)
video_file_name = "infinite_zoom_" + str(int(time.time())) + ".mp4"
output_path = shared.opts.data.get(
"infzoom_outpath", shared.opts.data.get("outdir_img2img_samples")
)
save_path = os.path.join(
output_path, shared.opts.data.get("infzoom_outSUBpath", "infinite-zooms")
)
if not os.path.exists(save_path):
os.makedirs(save_path)
out = os.path.join(save_path, video_file_name)
write_video(
out,
all_frames,
video_frame_rate,
video_zoom_mode,
int(video_start_frame_dupe_amount),
int(video_last_frame_dupe_amount),
)
return (
out,
processed.images,
processed.js(),
plaintext_to_html(processed.info),
plaintext_to_html(""),
)
def validatePromptJson_throws(data):
with open(jsonprompt_schemafile, "r") as s:
schema = json.load(s)
validate(instance=data, schema=schema)
def putPrompts(files):
try:
with open(files.name, "r") as f:
file_contents = f.read()
data = json.loads(file_contents)
validatePromptJson_throws(data)
return [
gr.DataFrame.update(data["prompts"]),
gr.Textbox.update(data["negPrompt"]),
]
except Exception:
gr.Error(
"loading your prompt failed. It seems to be invalid. Your prompt table is preserved."
)
print(
"[InfiniteZoom:] Loading your prompt failed. It seems to be invalid. Your prompt table is preserved."
)
return [gr.DataFrame.update(), gr.Textbox.update()]
def clearPrompts():
return [
gr.DataFrame.update(value=[[0, "Infinite Zoom. Start over"]]),
gr.Textbox.update(""),
]
def on_ui_tabs():
with gr.Blocks(analytics_enabled=False) as infinite_zoom_interface:
gr.HTML(
"""
<p style="text-align: center;">
<a target="_blank" href="https://github.com/v8hid/infinite-zoom-automatic1111-webui"><img src="https://img.shields.io/static/v1?label=github&message=repository&color=blue&style=flat&logo=github&logoColor=white" style="display: inline;" alt="GitHub Repo"/></a>
<a href="https://discord.gg/v2nHqSrWdW"><img src="https://img.shields.io/discord/1095469311830806630?color=blue&label=discord&logo=discord&logoColor=white" style="display: inline;" alt="Discord server"></a>
</p>
"""
)
with gr.Row():
generate_btn = gr.Button(value="Generate video", variant="primary")
interrupt = gr.Button(value="Interrupt", elem_id="interrupt_training")
with gr.Row():
with gr.Column(scale=1, variant="panel"):
with gr.Tab("Main"):
main_outpaint_steps = gr.Slider(
minimum=2,
maximum=100,
step=1,
value=8,
label="Total Outpaint Steps",
info="The more it is, the longer your videos will be",
)
# safe reading json prompt
pr = shared.opts.data.get("infzoom_defPrompt", default_prompt)
if not pr:
pr = empty_prompt
try:
jpr = json.loads(pr)
validatePromptJson_throws(jpr)
except Exception:
jpr = invalid_prompt
main_prompts = gr.Dataframe(
type="array",
headers=["outpaint step", "prompt"],
datatype=["number", "str"],
row_count=1,
col_count=(2, "fixed"),
value=jpr["prompts"],
wrap=True,
)
main_negative_prompt = gr.Textbox(
value=jpr["negPrompt"], label="Negative Prompt"
)
# these button will be moved using JS unde the dataframe view as small ones
exportPrompts_button = gr.Button(
value="Export prompts",
variant="secondary",
elem_classes="sm infzoom_tab_butt",
elem_id="infzoom_exP_butt",
)
importPrompts_button = gr.UploadButton(
label="Import prompts",
variant="secondary",
elem_classes="sm infzoom_tab_butt",
elem_id="infzoom_imP_butt",
)
exportPrompts_button.click(
None,
_js="exportPrompts",
inputs=[main_prompts, main_negative_prompt],
outputs=None,
)
importPrompts_button.upload(
fn=putPrompts,
outputs=[main_prompts, main_negative_prompt],
inputs=[importPrompts_button],
)
clearPrompts_button = gr.Button(
value="Clear prompts",
variant="secondary",
elem_classes="sm infzoom_tab_butt",
elem_id="infzoom_clP_butt",
)
clearPrompts_button.click(
fn=clearPrompts,
inputs=[],
outputs=[main_prompts, main_negative_prompt],
)
main_sampler = gr.Dropdown(
label="Sampler",
choices=available_samplers,
value="Euler a",
type="value",
)
with gr.Row():
main_width = gr.Slider(
minimum=16,
maximum=2048,
value=shared.opts.data.get("infzoom_outsizeW", 512),
step=16,
label="Output Width",
)
main_height = gr.Slider(
minimum=16,
maximum=2048,
value=shared.opts.data.get("infzoom_outsizeH", 512),
step=16,
label="Output Height",
)
with gr.Row():
main_guidance_scale = gr.Slider(
minimum=0.1,
maximum=15,
step=0.1,
value=7,
label="Guidance Scale",
)
sampling_step = gr.Slider(
minimum=1,
maximum=100,
step=1,
value=50,
label="Sampling Steps for each outpaint",
)
with gr.Row():
init_image = gr.Image(type="pil", label="custom initial image")
exit_image = gr.Image(
type="pil", label="custom exit image", visible=False
) # TODO: implement exit-image rendering
batchcount_slider = gr.Slider(
minimum=1,
maximum=25,
value=shared.opts.data.get("infzoom_batchcount", 1),
step=1,
label="Batch Count",
)
with gr.Tab("Video"):
video_frame_rate = gr.Slider(
label="Frames per second",
value=30,
minimum=1,
maximum=60,
)
video_zoom_mode = gr.Radio(
label="Zoom mode",
choices=["Zoom-out", "Zoom-in"],
value="Zoom-out",
type="index",
)
video_start_frame_dupe_amount = gr.Slider(
label="number of start frame dupe",
info="Frames to freeze at the start of the video",
value=0,
minimum=1,
maximum=60,
)
video_last_frame_dupe_amount = gr.Slider(
label="number of last frame dupe",
info="Frames to freeze at the end of the video",
value=0,
minimum=1,
maximum=60,
)
video_zoom_speed = gr.Slider(
label="Zoom Speed",
value=1.0,
minimum=0.1,
maximum=20.0,
step=0.1,
info="Zoom speed in seconds (higher values create slower zoom)",
)
with gr.Tab("Outpaint"):
inpainting_denoising_strength = gr.Slider(
label="Denoising Strength", minimum=0.75, maximum=1, value=1
)
inpainting_mask_blur = gr.Slider(
label="Mask Blur", minimum=0, maximum=64, value=0
)
inpainting_fill_mode = gr.Radio(
label="Masked content",
choices=["fill", "original", "latent noise", "latent nothing"],
value="latent noise",
type="index",
)
inpainting_full_res = gr.Checkbox(label="Inpaint Full Resolution")
inpainting_padding = gr.Slider(
label="masked padding", minimum=0, maximum=256, value=0
)
with gr.Tab("Post proccess"):
upscale_do = gr.Checkbox(False, label="Enable Upscale")
upscaler_name = gr.Dropdown(
label="Upscaler",
elem_id="infZ_upscaler",
choices=[x.name for x in shared.sd_upscalers],
value=shared.sd_upscalers[0].name,
)
upscale_by = gr.Slider(
label="Upscale by factor", minimum=1, maximum=8, value=1
)
with gr.Accordion("Help", open=False):
gr.Markdown(
"""# Performance critical
Depending on amount of frames and which upscaler you choose it might took a long time to render.
Our best experience and trade-off is the R-ERSGAn4x upscaler.
"""
)
with gr.Column(scale=1, variant="compact"):
output_video = gr.Video(label="Output").style(width=512, height=512)
(
out_image,
generation_info,
html_info,
html_log,
) = create_output_panel(
"infinite-zoom", shared.opts.outdir_img2img_samples
)
generate_btn.click(
fn=wrap_gradio_gpu_call(create_zoom, extra_outputs=[None, "", ""]),
inputs=[
main_prompts,
main_negative_prompt,
main_outpaint_steps,
main_guidance_scale,
sampling_step,
init_image,
exit_image,
video_frame_rate,
video_zoom_mode,
video_start_frame_dupe_amount,
video_last_frame_dupe_amount,
inpainting_denoising_strength,
inpainting_mask_blur,
inpainting_fill_mode,
inpainting_full_res,
inpainting_padding,
video_zoom_speed,
main_width,
main_height,
batchcount_slider,
main_sampler,
upscale_do,
upscaler_name,
upscale_by,
],
outputs=[output_video, out_image, generation_info, html_info, html_log],
)
interrupt.click(fn=lambda: shared.state.interrupt(), inputs=[], outputs=[])
infinite_zoom_interface.queue()
return [(infinite_zoom_interface, "Infinite Zoom", "iz_interface")]
def on_ui_settings():
section = ("infinite-zoom", "Infinite Zoom")
shared.opts.add_option(
"outputs"
"infzoom_outpath",
shared.OptionInfo(
"",
"Path where to store your infinite video. Default is Outputs",
gr.Textbox,
{"interactive": True},
section=section,
),
)
shared.opts.add_option(
"infzoom_outSUBpath",
shared.OptionInfo(
"infinite-zooms",
"Which subfolder name to be created in the outpath. Default is 'infinite-zooms'",
gr.Textbox,
{"interactive": True},
section=section,
),
)
shared.opts.add_option(
"infzoom_outsizeW",
shared.OptionInfo(
512,
"Default width of your video",
gr.Slider,
{"minimum": 16, "maximum": 2048, "step": 16},
section=section,
),
)
shared.opts.add_option(
"infzoom_outsizeH",
shared.OptionInfo(
512,
"Default height your video",
gr.Slider,
{"minimum": 16, "maximum": 2048, "step": 16},
section=section,
),
)
shared.opts.add_option(
"infzoom_ffprobepath",
shared.OptionInfo(
"",
"Writing videos has dependency to an existing FFPROBE executable on your machine. D/L here (https://github.com/BtbN/FFmpeg-Builds/releases) your OS variant and point to your installation path",
gr.Textbox,
{"interactive": True},
section=section,
),
)
shared.opts.add_option(
"infzoom_txt2img_model",
shared.OptionInfo(
None,
"Name of your desired model to render keyframes (txt2img)",
gr.Dropdown,
lambda: {"choices": shared.list_checkpoint_tiles()},
section=section,
),
)
shared.opts.add_option(
"infzoom_inpainting_model",
shared.OptionInfo(
None,
"Name of your desired inpaint model (img2img-inpaint). Default is vanilla sd-v1-5-inpainting.ckpt ",
gr.Dropdown,
lambda: {"choices": shared.list_checkpoint_tiles()},
section=section,
),
)
shared.opts.add_option(
"infzoom_defPrompt",
shared.OptionInfo(
default_prompt,
"Default prompt-setup to start with'",
gr.Code,
{"interactive": True, "language": "json"},
section=section,
),
)
from modules import script_callbacks
from iz_helpers.ui import on_ui_tabs
from iz_helpers.settings import on_ui_settings
script_callbacks.on_ui_tabs(on_ui_tabs)
script_callbacks.on_ui_settings(on_ui_settings)
script_callbacks.on_ui_settings(on_ui_settings)

View File

@ -1,49 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"prompts": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "array",
"items": [
{
"oneOf": [
{
"type": "integer",
"minimum": 0
},
{
"type": "string"
}
]
},
{
"type": "string"
}
],
"minItems": 0,
"maxItems": 999,
"uniqueItems": false
},
"minItems": 0
},
"headers": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 2
}
},
"required": ["data", "headers"]
},
"negPrompt": {
"type": "string"
}
},
"required": ["prompts", "negPrompt"]
}