Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aspect ratio sliders #7601

Merged
merged 4 commits into from
Feb 19, 2023
Merged

Conversation

Gerschel
Copy link
Contributor

@Gerschel Gerschel commented Feb 6, 2023

Sliders adjust according to aspect ratio.

Default choices added to settings in user interface section
Choices are editable by user

User selects from dropdown.
When you move one slider, the other adjusts according to the ratio chosen.
Vice versa for the other slider.

Number fields for changes work as well.

For disabling ratio, an unlock pad "🔓" is available as a default.
This string can be changed to anything to serve as a disable,
as long as there is no colon ":" in the string.

Ratios are entered in this format, floats or ints with a colon "1:1".
The string is split at the colon, parses left and right as floats to
perform the math.

More technical discussion about your changes go here, plus anything that a maintainer might have to specifically take a look at, or be wary of.
I included the componentController library, as this uses it.

Environment this was tested in

  • OS: Windows
  • Browser: [Chrome and Firefox]

Now for the demos:

Video demonstration:
https://user-images.githubusercontent.com/40751091/217967464-d9d9cb67-a195-4697-b11f-137d14cf42af.mp4

On settings tab:
image

On txt2img and img2img, it is a dropdown that looks like a button, it has an indicator that can hot swap the setting:
image

In the upper right corner is the indicator, it's clickable, this indicates it's in precision mode:
image

This indicates it's in rounding mode:
image
.
Changing round/precision mode literally changes and reads from the setting, making it hot swappable, no save and apply necessary unless you want to tell the app which value to use as default on start-up.
.
.
The reason it has two modes, models have a limitation of step 8. To keep a ratio intact and on a multiple of 8, we end up skipping 7 valid ratio coordinates for every perfect match. This is why there is rounding mode, so you can get close, and it rounds to a nearby step 8, which throws off the aspect ratio a little, but keeps you from having to do ratio math for every image, and keeps you from generating unnecessary stuff you'll crop out.

Rounding mode gives users options, some will want to round up, round nearest, or round down. This is to give them flexibility and makes it easier for users to know what behavior to expect when adjusting sliders.

.
.
The dropdown has choices of aspect ratios, these are configurable in settings:
image

When a ratio is selected in the dropdown/button, the sliders will initially snap to complimentary values, how accurate depends on your settings.

Then using either the sliders or number fields, the values will move accordingly (by precision/rounding and by ratio).

When you don't want them to have a ratio, there is an unlocked pad as a symbol, which is on by default. When the sliders are unlocked, it hides the indicator. This symbol can be changed in the settings as well.
image

The javascript class is setup to be reusable, just reference your element id's and call its static method for it to start it's own observer.

You will need to have two sliders, a dropdown that has your ratios, a checkbox for a rounding source and a radio for rounding method. This will allow others to implement it in their scripts/extensions.
image

Default choices added to settings in user interface section
Choices are editable by user

User selects from dropdown.
When you move one slider, the other adjusts according to the ratio
chosen.
Vice versa for the other slider.

Number fields for changes work as well.

For disabling ratio, an unlock pad "🔓" is available as a default.
This string can be changed to anything to serve as a disable,
as long as there is no colon ":".

Ratios are entered in this format, floats or ints with a colon "1:1".
The string is split at the colon, parses left and right as floats to
perform the math.
@Gerschel
Copy link
Contributor Author

Gerschel commented Feb 6, 2023

I added the "buttons" as a column. Styled the dropdown to remove the 'svg' background-image drodown (created by svelte), I included the appearance rules to remove the default dropdown arrow (but the svelte component does it already), just in case.

Here is the updated look.
image

@Gerschel Gerschel marked this pull request as ready for review February 6, 2023 19:57
@w-e-w
Copy link
Collaborator

w-e-w commented Feb 6, 2023

wow that very clean, nice work!

issue

resolution need to be in steps of 8

currently you're using Math.round() set it to the closest integer
but when the image is generated, the resolution is floored to the nearest multiple of 8
so if I set the aspect ratio to 16:9, the actual result will something likely be 16:8.95

personally I rather have 16:9.05 then 16:8.95
I think they should be an option to round up the number to the nearest multiple of 8
and I think rounding up the default option

@Gerschel
Copy link
Contributor Author

Gerschel commented Feb 6, 2023

I'm not sure how I would work the math for different ratios. I forget how to do math, the computer does it.
Something like, set the step size (of width) so it snaps at points where the other value (height) is a multiple of 8 and on the ratio.
Then set the step size the other way around (on height to affect width).

I feel like this is grade school math, something like greatest common denominator. 😆 Oh man.

@w-e-w
Copy link
Collaborator

w-e-w commented Feb 6, 2023

forget how to do math

lol

I feel like this is grade school math

yes it is 😆

function roundStepUp(number, step) {
    return Math.ceil(number / step ) * step;
}
console.log(roundStepUp(64, 8)) 	// 64
console.log(roundStepUp(64.1, 8)) 	// 72
console.log(roundStepUp(72, 8)) 	// 72
console.log(roundStepUp(127, 8)) 	// 128
console.log(roundStepUp(128, 8))	// 128
console.log(roundStepUp(129, 8))	// 136

forgot to mention there is no need to round down the number to an integer

@Gerschel
Copy link
Contributor Author

Gerschel commented Feb 6, 2023

I'm ready. Seems to be working. Have not tested it with a cinematic ratio. I forgot to remove the other 'rounds' that should not be needed.

@Gerschel
Copy link
Contributor Author

Gerschel commented Feb 6, 2023

@w-e-w Thanks for helping and holding me accountable. This is turning out alright.

@w-e-w
Copy link
Collaborator

w-e-w commented Feb 6, 2023

no not working

@w-e-w
Copy link
Collaborator

w-e-w commented Feb 6, 2023

wait a moment
you made another commit

@Gerschel
Copy link
Contributor Author

Gerschel commented Feb 6, 2023

Huh. It's acting like I didn't trigger the change event when I change the dropdown.

@w-e-w
Copy link
Collaborator

w-e-w commented Feb 6, 2023

nope, not working, still some issues
you can get values like 1000x563
image
563 is not a multiple of 8 and
and when it tries to generate a image of the size it freaks out
in my case it results in a image size of 1000x512

both numbers will have to be a multiple of 8

I'm not sure what you did but you seem to override the check that rounds down the number two multiples of 8
do not do so

in this case the resolution should be 1000x568
image
which correctly generated a image at the resolution of 1000x568

@w-e-w
Copy link
Collaborator

w-e-w commented Feb 6, 2023

input w1000 at 16:9 should output a H of 568
image

both numbers should be multiple of 8 otherwise it's freaks out

@Gerschel
Copy link
Contributor Author

Gerschel commented Feb 6, 2023

I made a change on the slider component controller. I noticed it wasn't updating the backend in all scenarios, just some.
I do get the 568 height when on 1000.
I think the base it steps up from get thrown off, if any number hits the minimum.

I'm going to explore my logic. I might've missed something small.

@anapnoe
Copy link

anapnoe commented Feb 6, 2023

Cool I like it

@Gerschel
Copy link
Contributor Author

Gerschel commented Feb 6, 2023

I noticed, when I use the up/down arrow in the number field, 16:9, it can change from 712 to 784 on width, while height stays at 424.
This means I need to rework the step logic for sure.

@5traverser
Copy link

Could you take a look at this? It uses step of 64 instead of 8
https://github.com/preyx/sd-scale-calc/blob/main/docs/index.html

@w-e-w
Copy link
Collaborator

w-e-w commented Feb 6, 2023

Could you take a look at this? It uses step of 64 instead of 8 https://github.com/preyx/sd-scale-calc/blob/main/docs/index.html

this thing looks quite old

see
https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/master/modules/ui.py#L479

webui did use step 64 couple months ago
but has since changed to use step 8 since

however if you are old user of webui backthen
since the config that controls the step value is stored in ui-config.json
the "step 8 update" will not apply
you will need to manually edit ui-config.json or delete it
letting the configuration file to be regenerated for you to have step slider in the UI

@w-e-w
Copy link
Collaborator

w-e-w commented Feb 7, 2023

https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/master/modules/processing.py#L35
I believe this is what ultimately controls the "step value"
the height and width of the image is integer divided by 8

sorry that I can only give suggestions and help debug
I really need to learn JS someday

@Gerschel
Copy link
Contributor Author

Gerschel commented Feb 7, 2023

I think I understand the issue, there was a conflict, and my stuff was off. I removed the conflict. I have it in my head on how to fix it, but have to spell it out.

@w-e-w
Copy link
Collaborator

w-e-w commented Feb 7, 2023

just in case there's any misunderstanding
it is my understanding that the network can only be used at resolution in multiple of 8 (or maybe 16 depending on the model)

@Gerschel Gerschel marked this pull request as draft February 7, 2023 05:29
@Gerschel
Copy link
Contributor Author

Gerschel commented Feb 7, 2023

@w-e-w
So. I had a solution working, thought it was wrong, but it was correct, went full circle. I constrained it to multiples of 8. So it leaps to valid values where both are a correct aspect ratio but of a multiple of 8.
Even though a number like width 1040, and height 585 are a valid 16:9 ratio, they are not both a multiple of 8.

The same with 528x297.
If I do an image at 528x297 without using this tool, I get a result of 528x296.
The question becomes, would someone using this tool expect it to show values that are valid, when they adjust the sliders and it snaps to points, that it will be the size it says.
I imagine that if a person was wanting a particular aspect ratio, that the outcome would keep it intact.
It's too bad that we are on the multiple of 8 constraint, because there would be other sizes that are valid.

I think it's ready for another round of checks, I tested it with 1.85:1, but I can't do high numbers (I temporary changed the max on the sliders, they work as long as you have enough memory, tested with 64x2496 to see if I can go above 2048, and yes).
But for 1.85:1, I can only test up to 1184x640.

@w-e-w
Copy link
Collaborator

w-e-w commented Feb 7, 2023

good news

did some quick test and didn't find any issues
I didn't find any bugs but I don't like it

bad news

I don't think enforcing that the aspect ratio is perfect is a good idea

first issue this makes this ratio slider FAR less useful

in an applications such as Photoshop or Gimp
if you set a specify ratio, it is expected that the number is rounded to integer
the use are not asking for Pixel Perfect ratio, as long as it's close enough
here due to stable diffusion we need to round up to the nearest 8 pixel but the concepts still applies

image
if I specify is 9:16 image I don't care if its 9:16 or 9:16.14, if I do really need exact 9:16 I can crop the image after
what a user would want is Convenient method to set a ratio close enough to 9:16 that the difference doesn't matter that much

and in my opinion I would prefer the ratio to be round UP rather than down,
image you want a image 9:16 with one side be w600, the ideal size will be 600x1067 (600x1066.667)
due to step 8 this H will need to be round up
and image will need to be 600x1072, I can easily crop this to 600x1067

but instead if I round down the image will be 600x1064, I will need to crop theis image to 599x1064 then scale it up by a small amount

note this is just an example and a bit exaggerated
8 pixel due to the relative size of the image doesn't really matter

the second issue of enforcing the perfect aspect ratio

for example 1:1.3
as you can see this tool becomes useless
image
(the slider is effectively not movable)

the solution

you simply need to round up the other number to the nearest multiple of 8

function roundStepUp(number, step) {
    return Math.ceil(number / step ) * step;
}

image
#7601 (comment)

however someone do prefer rounding down, you simply have to change ceil to floor
if you do wish you can make a toggle in the settings for round up or down but personally I wouldn't bother

maybe someone do wish exact ratio (though I don't see it), I think you can keep this under an option

just let the user deal which non exact aspect ratio, the same way that it has been done in graphic softwares
gimp
image
the 1:1.3 example is quite sane, compared to the ratios we use for paper
https://en.wikipedia.org/wiki/Paper_size

@Gerschel
Copy link
Contributor Author

Gerschel commented Feb 7, 2023

I don't think I'm going to round it off. Some will expect pixel perfect and some wont care. It does have the ability to put in a different ratio. So if it needs to be slightly off, it can be done there to change the scaling.
I think that process keeps it predictable and flexible.

@Gerschel
Copy link
Contributor Author

Gerschel commented Feb 7, 2023

1:1.3 is so close to 1:1, why not use 1:1, go next step up and trim 6 pixels off one edge.

@Gerschel
Copy link
Contributor Author

Gerschel commented Feb 7, 2023

I get it. I'm going to poke around a little and think about it.

@Gerschel Gerschel force-pushed the aspect_ratio_sliders branch from b030b67 to 374fe63 Compare February 9, 2023 03:01
@Gerschel Gerschel marked this pull request as ready for review February 9, 2023 03:01
@w-e-w
Copy link
Collaborator

w-e-w commented Feb 9, 2023

@AUTOMATIC1111 we made aspect ratio slider using a different approach from previous rejected PRs
this approach uses JavaScript on the client side to control the slider element directly
unlike previous implementations, the UI design it's very compact and fits in well with the existing design
minimum change using only wasted space

in my opinion it makes the UI looks better than before

by default it is not active and does not interfere with normal use
when user clicks on the button a drop-down menu appears allowing the user to choose the desired aspect ratio
after the ratio has been selected, when one slider is adjusted, the other slider will automatically be adjusted to fit the specified aspect ratio

the list of aspect ratio can be manually configured in the settings
the adjusted resolution is rounded to multiples of 8 so that the resolution is valid
the rounding method used can be further specified settings under user interface
by default it defaults the rounding method used is "ceiling"

I hope this PR will get merged

here is a short example of the implementation in action

old video new updated below
2023-02-09.23-30-06.Replay.mp4
also Android Chrome

@w-e-w
Copy link
Collaborator

w-e-w commented Feb 10, 2023

asking opinion from others were following this PR

the badger Icon is an indicator of whether or not the rounding mode is used
(whether or not the resolution is an exact ratio or rounded)

is there a better way of indicating this

different icons / backgrounds / colors etc, different types of indicator all together, or maybe even get rid of it?

the icon is also clickable, so you can switch between rounding or exact

I made a PR to Gerschel branch that change this
2023-02-10 08_41_49-Stable Diffusion 0070142023-02-10 08_41_56-Stable Diffusion 007015
to this
2023-02-10 08_31_21-Stable Diffusion 0070132023-02-10 08_31_13-Stable Diffusion 007012
in my opinion this makes interface looks cleaner

@w-e-w
Copy link
Collaborator

w-e-w commented Feb 10, 2023

2023-02-10.09-07-09.Replay.mp4

remove Badge background and ⚠️ ->📏
@Gerschel
Copy link
Contributor Author

I hated the symbol. I was using a round badge to indicate rounding, and a square badge to indicate ?sharper?, but it seemed to have issues depending on font size and character.
I was waiting for you to come up with some answer to it.
I can come up with some crazy solutions, but I have no artistic design.

@w-e-w
Copy link
Collaborator

w-e-w commented Feb 11, 2023

there seems to be something wrong with to github workflow test

other people's tests have also failed
the test passed on my machine

I was using a round badge to indicate rounding, and a square badge to indicate ?sharper?,

LOL, so that was what you were thinking, before looking at the code I thought it was some sort of drawing issue

there are tons of good emojis to choose from
but I'm not sure if they will convey the proper meaning

emojis

⬆️ Up Arrow
⬇️ Down Arrow
🔄 Counterclockwise Arrows Button
🔘 Radio Button
🔳 White Square Button
🔲 Black Square Button
🔺 Red Triangle Pointed Up
🔻 Red Triangle Pointed Down
🔴 Red Circle
🟠 Orange Circle
🟡 Yellow Circle
🟢 Green Circle
🔵 Blue Circle
🟣 Purple Circle
🟤 Brown Circle
⚫ Black Circle
⚪ White Circle
🟥 Red Square
🟧 Orange Square
🟨 Yellow Square
🟩 Green Square
🟦 Blue Square
🟪 Purple Square
🟫 Brown Square
⬛ Black Large Square
⬜ White Large Square

I gess if we really want to we can make the symbol customizable
but I think we should wait to see if all the AUTO accept the PR

@w-e-w
Copy link
Collaborator

w-e-w commented Feb 11, 2023

Current state of this feature can be found in this comment. => #7601 (comment)

@Gerschel you might want to update that, so AUTO doesn't get confused, no telling when he will be back
I suggest update the images in #7601 (comment) and maybe link to my video

@Gerschel
Copy link
Contributor Author

Gerschel commented Feb 11, 2023

I updated it and moved it to the head of this request. That way there is no having to dig and jump around.

I imagine Automatic1111 is doing a ui overhaul, code clean-up/restructure and some documentation. So he can do a proper release version and hopefully set some standards.
We will know soon, which might mean redoing some of this, but it's mostly self contained.

@AUTOMATIC1111 AUTOMATIC1111 merged commit b20f28e into AUTOMATIC1111:master Feb 19, 2023
@AUTOMATIC1111
Copy link
Owner

I pressed merge by accident. It's marked as merged but I reverted it.

There is way too much javascript for me in this. Merging this in means that I will also have to maintain it. The preferred way for those things is for them to be an extension. If something needs to be added into the main repo for the extension to work, that can be added in. The js library will not be added.

Additionally, this produces errors in the JS console of the browser; additionally, I have step of 64 for width/height sliders, and changing the aspect ratio option once undoes the 64 piel step until page reload.

@w-e-w
Copy link
Collaborator

w-e-w commented Feb 19, 2023

@AUTOMATIC1111 the width/height step has been set to 8 since PR #4978
however if the ui-config.json will override it
if you did not clear the file since then ,you will sill have step of 64
new installs all have of step 8 as default

it's been like this for months and I don't see people having issues
the step value from stable-diffusion is also step 8 by default
see: scripts/txt2img.py #L286 scripts/txt2img.py #L181

@AUTOMATIC1111
Copy link
Owner

My preference is having step of 64. I specifically set it to 64 for myself.

@w-e-w
Copy link
Collaborator

w-e-w commented Feb 19, 2023

My preference is having step of 64. I specifically set it to 64 for myself.

I see

I guess we can try and make it an extension then
it will most likely need something to be added ui.py
and when the extension is detected that element will be activated

need to look into it

@AUTOMATIC1111
Copy link
Owner

The img2img_size_toolbox column can be added via PR, everything else can be done in an extension. Possible cause for errors in Js console is that you don't set target for your created events. See updateInput in ui.js..

@Gerschel
Copy link
Contributor Author

I will try to come up with a way where it uses more of the backend (python). The first attempt did that, but had the annoying white flash on the components. After playing with a few other things, I believe that flash is becasue there is a moment where the dark mode css does not apply, which I believe is caused by lag by too many calls because of event type.

I'm aware of the potential need for setting the target (gradio-app/gradio#2981 (comment)), I thought my console errors were because of my symlinks getting broken with other projects. So I'll double check it. Thanks for informing me of seeing them on your end as well.

With the current structure, it is possible to swap the step size rounding to other numbers, but to make it clean, it should be set as a class variable and allow the value to be set by the user. If anyone knows where I can find the user's default value (8 or 64), that would be helpful, but I'll probably find it, so I can set a default.

The hardest part of converting from javascript to python would be the clickable badge. So I will need to think about this for a moment. I'm going to do a work sprint starting today until March 5th (home and garden show, emerald city comiccon build/dismantle and some other show in oregon), I wont have much time between now and then.

@w-e-w
Copy link
Collaborator

w-e-w commented Feb 19, 2023

I might give it a go to, (mostly an excuse for me to learn some javascript)
and likely I will reuse your code, hope you don't mind
I will try to keep you posted if I have any progress

@Gerschel
Copy link
Contributor Author

If you're trying to make it like an extension, use css to target the name in the dropdown and hide it, because it technically does not have a "run" method.
alemelis/sd-webui-ar#4 (comment)

There are two ScriptRunner instances in the webui, one is txt2img and the other is img2img, each of these instances will call the "show" method, to determine if they will show on it's page.

This means there are two instances of your python class.
Shared state between instances can be handled by using a class variable so both can read and write from. It's less important in this kind of script though, but notable if it's needed for passing the current setting between tabs (the class holds a reference to it's instances, and one instance can look up the other instance in this list and call its changes). That would be more of an advanced feature.

Javascript can be used to grab the dropdown and move it to the desired location. Instead of appendChild, you would either use prepend or "insertAdjacent" i.e. - myTargetContainer.insertAdjacent("afterbegin", myDrodown).

That can leave ui.py untouched, but is more overhead in javascript, as you'll need to grab the toolbox created by the python script, insert the swap button into the toolbox, then insert the toolbox after the width/height column.

Pretty much, all the python code that currently exists for this can be put into a python class. Target it with js.

let toolbox = gradioApp().getElementById("txt2img_size_toolbox")
toolbox.appendChild("txt2img_res_switch_btn");
gradioApp().getElementById("txt2img_column_size").insertAdjacent("afterend", toolbox)

If trying to do it as a built-in, javascript maths can be moved to python, either use existing js functions provided by the webui, or embed a minimal amount in the python components _js method to link them.
But the hot changing badge might have to be changed into a separate button, or it will need js to pluck it and set it as a badge.

@w-e-w
Copy link
Collaborator

w-e-w commented Feb 19, 2023

thanks for the advice

I'm preparing myself to fail spectacularly

@SeanBannister
Copy link

@w-e-w any update on a potential extension. For anyone looking for something basic I found https://github.com/alemelis/sd-webui-ar

@w-e-w
Copy link
Collaborator

w-e-w commented Mar 10, 2023

@w-e-w any update on a potential extension. For anyone looking for something basic I found https://github.com/alemelis/sd-webui-ar

@SeanBannister
the previous implementation got accidentally merge then reverted by AUTO

essentially he doesn't want too much JavaScript in the main webui, because if so he would have to support it in the future

he said that we could make it a plug-in and if anything that "needs" to be added into the main repo it can

as for progress
I don't know about Gerschel, but I'm on holiday so not working on anything at the moment

@thomasasfk
Copy link

thomasasfk commented Mar 20, 2023

I created this extension for my own usage in-case anyone is interested: https://github.com/thomasasfk/sd-webui-aspect-ratio-helper

@w-e-w
Copy link
Collaborator

w-e-w commented Mar 20, 2023

I created this for my own usage in-case anyone is interested: https://github.com/thomasasfk/sd-webui-aspect-ratio-helper

https://github.com/AUTOMATIC1111/stable-diffusion-webui-extensions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants