Endless-Iterations commited on
Commit
b094968
1 Parent(s): a0bc898

Upload 43 files

Browse files
Files changed (44) hide show
  1. .gitattributes +7 -0
  2. sd-webui-vectorscope-cc/.gitattributes +2 -0
  3. sd-webui-vectorscope-cc/.github/FUNDING.yml +1 -0
  4. sd-webui-vectorscope-cc/CHANGELOG.md +76 -0
  5. sd-webui-vectorscope-cc/LICENSE +21 -0
  6. sd-webui-vectorscope-cc/README.md +250 -0
  7. sd-webui-vectorscope-cc/javascript/vec_cc.js +136 -0
  8. sd-webui-vectorscope-cc/samples/00.jpg +0 -0
  9. sd-webui-vectorscope-cc/samples/01.jpg +0 -0
  10. sd-webui-vectorscope-cc/samples/02.jpg +0 -0
  11. sd-webui-vectorscope-cc/samples/03.jpg +0 -0
  12. sd-webui-vectorscope-cc/samples/04.jpg +0 -0
  13. sd-webui-vectorscope-cc/samples/Bright.jpg +3 -0
  14. sd-webui-vectorscope-cc/samples/Dark.jpg +3 -0
  15. sd-webui-vectorscope-cc/samples/Random.jpg +3 -0
  16. sd-webui-vectorscope-cc/samples/Scaling.jpg +3 -0
  17. sd-webui-vectorscope-cc/samples/Scaling_alt.jpg +3 -0
  18. sd-webui-vectorscope-cc/samples/Skip.jpg +3 -0
  19. sd-webui-vectorscope-cc/samples/XYZ.jpg +3 -0
  20. sd-webui-vectorscope-cc/samples/api_example.json +29 -0
  21. sd-webui-vectorscope-cc/scripts/Vectorscope.png +0 -0
  22. sd-webui-vectorscope-cc/scripts/__pycache__/cc.cpython-310.pyc +0 -0
  23. sd-webui-vectorscope-cc/scripts/__pycache__/cc_colorpicker.cpython-310.pyc +0 -0
  24. sd-webui-vectorscope-cc/scripts/__pycache__/cc_const.cpython-310.pyc +0 -0
  25. sd-webui-vectorscope-cc/scripts/__pycache__/cc_hdr.cpython-310.pyc +0 -0
  26. sd-webui-vectorscope-cc/scripts/__pycache__/cc_noise.cpython-310.pyc +0 -0
  27. sd-webui-vectorscope-cc/scripts/__pycache__/cc_scaling.cpython-310.pyc +0 -0
  28. sd-webui-vectorscope-cc/scripts/__pycache__/cc_settings.cpython-310.pyc +0 -0
  29. sd-webui-vectorscope-cc/scripts/__pycache__/cc_style.cpython-310.pyc +0 -0
  30. sd-webui-vectorscope-cc/scripts/__pycache__/cc_version.cpython-310.pyc +0 -0
  31. sd-webui-vectorscope-cc/scripts/__pycache__/cc_xyz.cpython-310.pyc +0 -0
  32. sd-webui-vectorscope-cc/scripts/cc.py +294 -0
  33. sd-webui-vectorscope-cc/scripts/cc_colorpicker.py +23 -0
  34. sd-webui-vectorscope-cc/scripts/cc_const.py +12 -0
  35. sd-webui-vectorscope-cc/scripts/cc_hdr.py +107 -0
  36. sd-webui-vectorscope-cc/scripts/cc_noise.py +50 -0
  37. sd-webui-vectorscope-cc/scripts/cc_scaling.py +28 -0
  38. sd-webui-vectorscope-cc/scripts/cc_settings.py +6 -0
  39. sd-webui-vectorscope-cc/scripts/cc_style.py +76 -0
  40. sd-webui-vectorscope-cc/scripts/cc_version.py +20 -0
  41. sd-webui-vectorscope-cc/scripts/cc_xyz.py +40 -0
  42. sd-webui-vectorscope-cc/scripts/dot.png +0 -0
  43. sd-webui-vectorscope-cc/style.css +37 -0
  44. sd-webui-vectorscope-cc/styles.json +1 -0
.gitattributes CHANGED
@@ -33,3 +33,10 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ sd-webui-vectorscope-cc/samples/Bright.jpg filter=lfs diff=lfs merge=lfs -text
37
+ sd-webui-vectorscope-cc/samples/Dark.jpg filter=lfs diff=lfs merge=lfs -text
38
+ sd-webui-vectorscope-cc/samples/Random.jpg filter=lfs diff=lfs merge=lfs -text
39
+ sd-webui-vectorscope-cc/samples/Scaling_alt.jpg filter=lfs diff=lfs merge=lfs -text
40
+ sd-webui-vectorscope-cc/samples/Scaling.jpg filter=lfs diff=lfs merge=lfs -text
41
+ sd-webui-vectorscope-cc/samples/Skip.jpg filter=lfs diff=lfs merge=lfs -text
42
+ sd-webui-vectorscope-cc/samples/XYZ.jpg filter=lfs diff=lfs merge=lfs -text
sd-webui-vectorscope-cc/.gitattributes ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ *.png filter=lfs diff=lfs merge=lfs -text
2
+ *.jpg filter=lfs diff=lfs merge=lfs -text
sd-webui-vectorscope-cc/.github/FUNDING.yml ADDED
@@ -0,0 +1 @@
 
 
1
+ ko_fi: haoming
sd-webui-vectorscope-cc/CHANGELOG.md ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ### v1.4.10 - 2023 Nov.01
2
+ - Better **Hires. fix** logic
3
+
4
+ ### v1.4.9 - 2023 Nov.01
5
+ - Improve Sliders values refresh
6
+
7
+ ### v1.4.8 - 2023 Nov.01
8
+ - Removed "**magic numbers**"
9
+
10
+ ### v1.4.7 - 2023 Nov.01
11
+ - Removed **Skip** parameter
12
+
13
+ ### v1.4.6 - 2023 Sep.19
14
+ - Add **HDR** Script
15
+
16
+ ### v1.4.5 - 2023 Sep.13
17
+ - Bug Fix for Color Wheel in `img2img`
18
+ - Minor Formatting
19
+
20
+ ### v1.4.4 - 2023 Sep.13
21
+ - Add **Infotext** Support by. **catboxanon**
22
+
23
+ ### v1.4.3 - 2023 Sep.13
24
+ - Improve **Color Wheel** Functionality by. **catboxanon**
25
+
26
+ ### v1.4.2 - 2023 Sep.11
27
+ - Fix the Reset and Randomize buttons for the new Contrast algorithm
28
+
29
+ ### v1.4.1 - 2023 Sep.11
30
+ - New **Contrast** algorithm
31
+
32
+ ### v1.4.0 - 2023 Jul.11
33
+ - Implement **Scaling** algorithm
34
+
35
+ ### v1.3.5 - 2023 Jul.11
36
+ - Implement **Color Picker**
37
+
38
+ ### v1.3.4 - 2023 Jul.08
39
+ - Implement **Metadata**
40
+
41
+ ### v1.3.3 - 2023 Jul.07
42
+ - Color Wheel now works at (0, 0, 0)
43
+ - Style/Randomize/Reset now updates Color Wheel
44
+
45
+ ### v1.3.2 - 2023 Jul.07
46
+ - Implement **Color Wheel**
47
+ - Finally added `changelog`
48
+
49
+ ### v1.3.1 - 2023 Jul.06
50
+ - Bug Fix
51
+
52
+ ### v1.3.0 - 2023 Jul.06
53
+ - Implement **Style Presets**
54
+ - Separate logics into different scripts
55
+
56
+ ### v1.2.1 - 2023 Jul.05
57
+ - Add **Randomize** function
58
+ - Add **Reset** function
59
+
60
+ ### v1.2.0 - 2023 Jul.03
61
+ - Implement multiple **Noise** algorithms
62
+
63
+ ### v1.1.3 - 2023 Jun.26
64
+ - Automatically refresh outdated Sliders values
65
+
66
+ ### v1.1.2 - 2023 Jun.23
67
+ - Bug Fix
68
+
69
+ ### v1.1.1 - 2023 Jun.22
70
+ - **Batch** Support
71
+
72
+ ### v1.1.0 - 2023 Jun.21
73
+ - **X/Y/Z Plot** Support
74
+
75
+ ### v1.0.0 - 2023 Jun.20
76
+ - Extension **Released**!
sd-webui-vectorscope-cc/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Haoming
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
sd-webui-vectorscope-cc/README.md ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SD Webui Vectorscope CC
2
+ This is an Extension for the [Automatic1111 Webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui), which performs a kind of **Offset Noise**[*](#offset-noise-tldr) natively,
3
+ allowing you to adjust the brightness, contrast, and color of the generations.
4
+
5
+ > [Sample Images](#sample-images)
6
+
7
+ ## How to Use
8
+ After installing this Extension, you will see a new section in both **txt2img** and **img2img** tabs.
9
+ Refer to the parameters and sample images below and play around with the values.
10
+
11
+ **Note:** Since this modifies the underlying latent noise, the composition may change drastically. Using the **Ones** scaling seems to reduce the variations.
12
+
13
+ #### Parameters
14
+ - **Enable:** Turn on/off this Extension
15
+ - **Alt:** Modify an alternative Tensor instead, causing the effects to be significantly stronger
16
+ - **Brightness:** Adjust the overall brightness of the image
17
+ - **Contrast:** Adjust the overall contrast of the image
18
+ - **Saturation:** Adjust the overall saturation of the image
19
+
20
+ #### Color Channels
21
+ - Comes with a Color Wheel for visualization
22
+ - You can also click and drag on the Color Wheel to select a color directly
23
+
24
+ <table>
25
+ <thead align="center">
26
+ <tr>
27
+ <td><b>Channel</b></td>
28
+ <td><b>Lower</b></td>
29
+ <td><b>Higher</b></td>
30
+ </tr>
31
+ </thead>
32
+ <tbody align="center">
33
+ <tr>
34
+ <td><b>R</b></td>
35
+ <td>Cyan</td>
36
+ <td>Red</td>
37
+ </tr>
38
+ <tr>
39
+ <td><b>G</b></td>
40
+ <td>Magenta</td>
41
+ <td>Green</td>
42
+ </tr>
43
+ <tr>
44
+ <td><b>B</b></td>
45
+ <td>Yellow</td>
46
+ <td>Blue</td>
47
+ </tr>
48
+ </tbody>
49
+ </table>
50
+
51
+ #### Buttons
52
+ - **Reset:** Reset all settings to the default values
53
+ - **Randomize:** Randomize `Brightness`, `Contrast`, `Saturation`, `R`, `G`, `B`
54
+
55
+ #### Style Presets
56
+ - Use the `Dropdown` to select a Style then click **Apply Style** to apply
57
+ - To save a Style, enter a name in the `Textbox` then click **Save Style**
58
+ - To delete a Style, enter the name in the `Textbox` then click **Delete Style**
59
+ - *Deleted Style is still in the `styles.json` in case you wish to retrieve it*
60
+ - Click **Refresh Style** to update the `Dropdown` if you edited the `styles.json` directly
61
+
62
+ #### Advanced Settings
63
+ - **Process Hires. fix:** By default, this Extension only functions during the **txt2img** phase, so that **Hires. fix** may "fix" the artifacts introduced during **txt2img**. Enable this to process **Hires. fix** phase too.
64
+ - This option does not affect **img2img**
65
+
66
+ ##### Noise Settings
67
+ > let `x` denote the Tensor ; let `y` denote the operations
68
+
69
+ <!-- "Straight", "Straight Abs.", "Cross", "Cross Abs.", "Ones", "N.Random", "U.Random", "Multi-Res", "Multi-Res Abs." -->
70
+
71
+ - **Straight:** All operations are calculated on the same Tensor
72
+ - `x += x * y`
73
+ - **Cross:** All operations are calculated on the Tensor opposite of the `Alt.` setting
74
+ - `x += x' * y`
75
+ - **Ones:** All operations are calculated on a Tensor filled with ones
76
+ - `x += 1 * y`
77
+ - **N.Random:** All operations are calculated on a Tensor filled with random values from normal distribution
78
+ - `x += randn() * y`
79
+ - **U.Random:** All operations are calculated on a Tensor filled with random values from uniform distribution
80
+ - `x += rand() * y`
81
+ - **Multi-Res:** All operations are calculated on a Tensor generated with multi-res noise algorithm
82
+ - `x += multires() * y`
83
+ - **Abs:** Calculate using the absolute values of the chosen Tensors instead
84
+ - `x += abs(F) * y`
85
+
86
+ <p align="center"><img src="samples/Bright.jpg" width=768></p>
87
+ <p align="center"><img src="samples/Dark.jpg" width=768></p>
88
+
89
+ ##### Scaling Settings
90
+ Previously, this Extension offsets the noise by the same amount each step.
91
+ But due to the denoising process, this may produce undesired outcomes such as blurriness at high **Brightness** or noises at low **Brightness**.
92
+ Thus, I added a scaling option to modify the offset amount.
93
+
94
+ > Essentially, the "magnitude" of the default Tensor gets smaller every step, so offsetting by the same amount will have stronger effects at later steps. This is reversed on the `Alt.` Tensor however.
95
+
96
+ - **Flat:** Default behavior. Same amount each step.
97
+ - **Cos:** Cosine scaling. *(High -> Low)*
98
+ - **Sin:** Sine scaling. *(Low -> High)*
99
+ - **1 - Cos:** *(Low -> High)*
100
+ - **1 - Sin:** *(High -> Low)*
101
+
102
+ > In my experience, **`1 - Sin`** works better for the **default** Tensor while **`1 - Cos`** works better for the **Alt.** Tensor
103
+
104
+ <p align="center">
105
+ <code>Alt. Disabled</code><br>
106
+ <img src="samples/Scaling.jpg" width=768>
107
+ </p>
108
+
109
+ <p align="center">
110
+ <code>Alt. Enabled</code><br>
111
+ <img src="samples/Scaling_alt.jpg" width=768>
112
+ </p>
113
+
114
+ <p align="center"><i>Notice the blurriness and the noises on <code>Flat</code> scaling</i></p>
115
+
116
+ ## Sample Images
117
+ - **Checkpoint:** [UHD-23](https://civitai.com/models/22371/uhd-23)
118
+ - **Pos. Prompt:** `(masterpiece, best quality), 1girl, solo, night, street, city, neon_lights`
119
+ - **Neg. Prompt:** `(low quality, worst quality:1.2)`, [`EasyNegative`](https://huggingface.co/datasets/gsdf/EasyNegative/tree/main), [`EasyNegativeV2`](https://huggingface.co/gsdf/Counterfeit-V3.0/tree/main/embedding)
120
+ - `Euler a`; `20 steps`; `7.5 CFG`; `Hires. fix`; `Latent (nearest)`; `16 H.steps`; `0.6 D.Str.`; `Seed:`**`3814649974`**
121
+ - `Straight Abs.`
122
+
123
+ <p align="center">
124
+ <b>Base</b><br>
125
+ <code>Extension Disabled</code><br>
126
+ <img src="samples/00.jpg" width=512>
127
+ </p>
128
+
129
+ <p align="center">
130
+ <b>Dark</b><br>
131
+ <code><b>Brightness:</b> -3; <b>Contrast:</b> 1.5</code><br>
132
+ <img src="samples/01.jpg" width=512>
133
+ </p>
134
+
135
+ <p align="center">
136
+ <b>Bright</b><br>
137
+ <code><b>Brightness:</b> 2.5; <b>Contrast:</b> 0.5; <b>Alt:</b> Enabled</code><br>
138
+ <img src="samples/02.jpg" width=512>
139
+ </p>
140
+
141
+ <p align="center">
142
+ <b>Chill</b><br>
143
+ <code><b>Brightness:</b> -2.5; <b>Contrast:</b> 1.25</code><br>
144
+ <code><b>R:</b> -1.5; <b>B:</b> 2.5</code><br>
145
+ <img src="samples/03.jpg" width=512>
146
+ </p>
147
+
148
+ <p align="center">
149
+ <b><s>Mexican Movie</s></b><br>
150
+ <code><b>Brightness:</b> 3; <b>Saturation:</b> 1.5</code><br>
151
+ <code><b>R:</b> 2; <b>G:</b> 1; <b>B:</b> -2</code><br>
152
+ <img src="samples/04.jpg" width=512>
153
+ </p>
154
+
155
+ <p align="center"><i>Notice the significant differences even when using the same seed</i></p>
156
+
157
+ ## Roadmap
158
+ - [X] Extension Released
159
+ - [X] Add Support for **X/Y/Z Plot**
160
+ - [X] Implement different **Noise** functions
161
+ - [X] Add **Randomize** functions
162
+ - [X] Append Parameters onto Metadata
163
+ - You can enable this in the **Infotext** section of the **Settings** tab
164
+ - [X] **Style** Presets
165
+ - [X] Implement **Color Wheel** & **Color Picker**
166
+ - [X] Implement better scaling algorithms
167
+ - [X] Fix the **Brightness** issues *~~kinda~~*
168
+ - [X] Add API Docs
169
+ - [X] Add Infotext Support *(by. [catboxanon](https://github.com/catboxanon))*
170
+ - [X] ADD **HDR** Script
171
+ - [ ] Add Gradient features
172
+
173
+ <p align="center"><code>X/Y/Z Plot Support</code><br><i>(Outdated Contrast Value)</i></p>
174
+ <p align="center"><img src="samples/XYZ.jpg" width=768></p>
175
+
176
+ <p align="center"><code>X/Y/Z Plot w/ Randomize</code></p>
177
+ <p align="center"><img src="samples/Random.jpg" width=768></p>
178
+ <p align="center">The value is used as the random seed<br>You can refer to the console to see the randomized values</p>
179
+
180
+ ## API
181
+ You can use this Extension via [API](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/API) by adding an entry in the `alwayson_scripts` of your payload.
182
+ An [example](samples/api_example.json) is provided.
183
+ The `args` are sent in the following order:
184
+
185
+ - **[Enable, Alt, Brightness, Contrast, Saturation, R, G, B, Process Hires. Fix, Noise Settings, Scaling Settings]**
186
+ > `bool`, `bool`, `float`, `float`, `float`, `float`, `float`, `float`, `bool`, `str`, `str`
187
+
188
+ ## Known Issues
189
+ - Does not work with `DDIM`, `UniPC` samplers
190
+ - Has little effect when used with certain **LoRA**s
191
+
192
+ ## HDR
193
+ <p align="right"><i><b>BETA</b></i></p>
194
+
195
+ > [Discussion Thread](https://github.com/Haoming02/sd-webui-vectorscope-cc/issues/16)
196
+
197
+ - In the **Script** `Dropdown` at the bottom, there is now a new option: **`High Dynamic Range`**
198
+ - This script will generate multiple images *("Brackets")* of varying brightness, then merge them into 1 HDR image
199
+ - *Do provide feedback in the thread!*
200
+ - **Highly Recommended** to use a deterministic sampler and high enough steps. `Euler` *(**not** `Euler a`)* worked the best in my experience.
201
+
202
+ #### Settings
203
+ - **Brackets:** The numer of images to generate
204
+ - **Gaps:** The brightness difference between each image
205
+ - **Automatically Merge:** When enabled, this will merge the images using a `OpenCV` algorithm and save to the `HDR` folder in the `outputs` folder; When disabled, this will return all images to the result section, for when you have a more advanced program such as Photoshop to do the merging.
206
+ - All the images are still saved to the `outputs` folder regardless
207
+
208
+ <hr>
209
+
210
+ ### Offset Noise TL;DR
211
+ The most common *version* of **Offset Noise** you may have heard of is from this [blog post](https://www.crosslabs.org/blog/diffusion-with-offset-noise),
212
+ where it was discovered that the noise functions used during **training** were flawed, causing `Stable Diffusion` to always generate images with an average of `0.5`.
213
+
214
+ > **ie.** Even if you prompt for dark/night or bright/snow, the overall image still looks "grey"
215
+
216
+ > [Technical Explanations](https://youtu.be/cVxQmbf3q7Q)
217
+
218
+ However, this Extension instead tries to offset the latent noise during the **inference** phase.
219
+ Therefore, you do not need to use models that were specially trained, as this can work on any model.
220
+ Though, the results may not be as good as using properly trained models.
221
+
222
+ <hr>
223
+
224
+ ### What is Under the Hood
225
+ After reading through and messing around with the code,
226
+ I found out that it is possible to directly modify the Tensors
227
+ representing the latent noise used by the Stable Diffusion process.
228
+
229
+ The dimensions of the Tensors is `(X, 4, H / 8, W / 8)`, which can be thought of like this:
230
+
231
+ > **X** batch of noise images, with **4** channels, each with **(W / 8) x (H / 8)** values
232
+
233
+ > **eg.** Generating a single 512x768 image will create a Tensor of size (1, 4, 96, 64)
234
+
235
+ Then, I tried to play around with the values of each channel and ended up discovering these relationships.
236
+ Essentially, the 4 channels correspond to the **CMYK** color format,
237
+ hence why you can control the brightness as well as the colors.
238
+
239
+ <hr>
240
+
241
+ ### Vectorscope?
242
+ The Extension is named this way because the color interactions remind me of the `Vectorscope` found in **Premiere Pro**'s **Lumetri Color**.
243
+ Those who are experienced in Color Correction should be rather familiar with this Extension.
244
+
245
+ <p align="center"><img src="scripts/Vectorscope.png" width=256></p>
246
+
247
+ <hr>
248
+
249
+ <sup>~~Yes. I'm aware that it's just how digital colors work in general.~~<br>
250
+ ~~We've come full **circle** *(\*ba dum tss)* now that a Color Wheel is actually added.~~</sup>
sd-webui-vectorscope-cc/javascript/vec_cc.js ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function registerPicker(wheel, sliders, mode) {
2
+ for (const event of ['mousemove', 'click']) {
3
+ wheel.addEventListener(event, (e) => {
4
+ e.preventDefault();
5
+
6
+ const rect = e.target.getBoundingClientRect();
7
+
8
+ if (e.type != 'click') {
9
+ if (e.buttons != 1) {
10
+ return;
11
+ }
12
+
13
+ const dot = e.target.parentElement.querySelector('#cc-dot-' + mode);
14
+ dot.style.position = 'fixed';
15
+ dot.style.left = e.x - (dot.width / 2) + 'px';
16
+ dot.style.top = e.y - (dot.height / 2) + 'px';
17
+ }
18
+
19
+ x = ((e.clientX - rect.left) - 100.0) / 25;
20
+ y = ((e.clientY - rect.top) - 100.0) / 25;
21
+
22
+ const zeta = Math.atan(y / x)
23
+ var degree = 0
24
+
25
+ if (x >= 0) {
26
+ if (y >= 0)
27
+ degree = zeta * 180 / Math.PI
28
+ else
29
+ degree = 360 + zeta * 180 / Math.PI
30
+ }
31
+ else if (x < 0) {
32
+ degree = 180 + zeta * 180 / Math.PI
33
+ }
34
+
35
+ // -0.5r - 0.5g + b = x
36
+ // -0.866r + 0.866g = y
37
+ // 240r + 120g = z * rgb
38
+
39
+ // g = (1 / 0.866)y + r
40
+ // -0.5r - 0.5((1 / 0.866)y + r) + b = x
41
+ // b = x + 0.5r + 0.5((1 / 0.866)y + r)
42
+
43
+ // 240r + 120(1 / 0.866)y + r = z * r((1 / 0.866)y + r)(x + 0.5r + 0.5((1 / 0.866)y + r))
44
+
45
+ var r = -(0.00077 * (433 * x * degree + 750 * y * degree) / degree)
46
+ var g = y / 0.866 + r
47
+ var b = x + 0.5 * r + 0.5 * g
48
+
49
+ const mag = Math.sqrt(r * r + g * g + b * b)
50
+ const len = Math.abs(r) + Math.abs(g) + Math.abs(b)
51
+
52
+ r = r / mag * len
53
+ g = g / mag * len
54
+ b = b / mag * len
55
+
56
+ sliders[0].value = r.toFixed(2)
57
+ sliders[0].closest('.gradio-slider').querySelector('input[type=range]').value = r.toFixed(2)
58
+ sliders[1].value = g.toFixed(2)
59
+ sliders[1].closest('.gradio-slider').querySelector('input[type=range]').value = g.toFixed(2)
60
+ sliders[2].value = b.toFixed(2)
61
+ sliders[2].closest('.gradio-slider').querySelector('input[type=range]').value = b.toFixed(2)
62
+
63
+ if (e.type == 'click') {
64
+ updateInput(sliders[0])
65
+ updateInput(sliders[1])
66
+ updateInput(sliders[2])
67
+ }
68
+ })
69
+ }
70
+
71
+ wheel.addEventListener('mouseup', (e) => {
72
+ const dot = e.target.parentElement.querySelector('#cc-dot-' + mode);
73
+ dot.style.position = 'absolute';
74
+ updateInput(sliders[0])
75
+ updateInput(sliders[1])
76
+ updateInput(sliders[2])
77
+ })
78
+ }
79
+
80
+ onUiLoaded(async () => {
81
+
82
+ ['txt', 'img'].forEach((mode) => {
83
+
84
+ const container = document.getElementById('cc-colorwheel-' + mode)
85
+ container.style.height = '200px'
86
+ container.style.width = 'auto'
87
+
88
+ container.querySelector('.float')?.remove()
89
+ container.querySelector('.download')?.remove()
90
+ for (const downloadButton of container.querySelectorAll('[download]')) {
91
+ downloadButton.parentElement.remove()
92
+ }
93
+
94
+ const wheel = container.getElementsByTagName('img')[0]
95
+ wheel.style.height = '100%'
96
+ wheel.style.width = 'auto'
97
+ wheel.style.margin = 'auto'
98
+ wheel.id = 'cc-img-' + mode
99
+
100
+ wheel.ondragstart = (e) => { e.preventDefault(); }
101
+
102
+ sliders = [
103
+ document.getElementById('cc-r-' + mode).querySelector('input'),
104
+ document.getElementById('cc-g-' + mode).querySelector('input'),
105
+ document.getElementById('cc-b-' + mode).querySelector('input')
106
+ ]
107
+
108
+ registerPicker(wheel, sliders, mode)
109
+
110
+ const temp = document.getElementById('cc-temp-' + mode)
111
+
112
+ const dot = temp.getElementsByTagName('img')[0]
113
+ dot.id = 'cc-dot-' + mode
114
+
115
+ container.appendChild(dot)
116
+ dot.style.left = 'calc(50% - 12px)'
117
+ dot.style.top = 'calc(50% - 12px)'
118
+
119
+ temp.remove()
120
+
121
+ const row1 = document.getElementById('cc-apply-' + mode).parentNode
122
+ const row2 = document.getElementById('cc-save-' + mode).parentNode
123
+
124
+ row1.style.alignItems = 'end'
125
+ row1.style.gap = '1em'
126
+ row2.style.alignItems = 'end'
127
+ row2.style.gap = '1em'
128
+
129
+ // ----- HDR UIs -----
130
+ const hdr_settings = document.getElementById('vec-hdr-' + mode)
131
+ const buttons = hdr_settings.getElementsByTagName('label')
132
+
133
+ for (let i = 0; i < buttons.length; i++)
134
+ buttons[i].style.borderRadius = '0.5em'
135
+ })
136
+ })
sd-webui-vectorscope-cc/samples/00.jpg ADDED
sd-webui-vectorscope-cc/samples/01.jpg ADDED
sd-webui-vectorscope-cc/samples/02.jpg ADDED
sd-webui-vectorscope-cc/samples/03.jpg ADDED
sd-webui-vectorscope-cc/samples/04.jpg ADDED
sd-webui-vectorscope-cc/samples/Bright.jpg ADDED

Git LFS Details

  • SHA256: 2ddc47a49da23c82a866fb3eff0771d8148f6b0a8d43343b7098ffcb451d98e1
  • Pointer size: 132 Bytes
  • Size of remote file: 2.97 MB
sd-webui-vectorscope-cc/samples/Dark.jpg ADDED

Git LFS Details

  • SHA256: 6151408d1d5c06d47789d5d5e7dec486b2c75dc57c90b3a68675c2f98ac794c0
  • Pointer size: 132 Bytes
  • Size of remote file: 5.79 MB
sd-webui-vectorscope-cc/samples/Random.jpg ADDED

Git LFS Details

  • SHA256: f81abf07ea51cd56896fa5fac6e7893c320e1a00a3a28db6ded3fe381da5f676
  • Pointer size: 132 Bytes
  • Size of remote file: 1.31 MB
sd-webui-vectorscope-cc/samples/Scaling.jpg ADDED

Git LFS Details

  • SHA256: 5bda9ab17d2debb368e51427761e6a18aeda0b5d892b5ae1fe4e1744850786ba
  • Pointer size: 132 Bytes
  • Size of remote file: 1.12 MB
sd-webui-vectorscope-cc/samples/Scaling_alt.jpg ADDED

Git LFS Details

  • SHA256: ea7a374885f6ac0c857de3966d44645b08fc729b5b1b406b070b3530f17555fa
  • Pointer size: 132 Bytes
  • Size of remote file: 1.06 MB
sd-webui-vectorscope-cc/samples/Skip.jpg ADDED

Git LFS Details

  • SHA256: c574778b94c76501c07569b13d30138191d37108b42d42c8d30220c5f23a4489
  • Pointer size: 132 Bytes
  • Size of remote file: 1.33 MB
sd-webui-vectorscope-cc/samples/XYZ.jpg ADDED

Git LFS Details

  • SHA256: 69d92a7b6649a4150fddbdaa3a602dade2a3a9fa7dab57b2094d401886bde4ed
  • Pointer size: 132 Bytes
  • Size of remote file: 2.2 MB
sd-webui-vectorscope-cc/samples/api_example.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "prompt": "(masterpiece, best quality), 1girl, solo, night",
3
+ "negative_prompt": "(low quality, worst quality:1.2)",
4
+ "seed": -1,
5
+ "sampler_name": "Euler a",
6
+ "sampler_index": "euler",
7
+ "batch_size": 1,
8
+ "steps": 24,
9
+ "cfg_scale": 8.5,
10
+ "width": 512,
11
+ "height": 512,
12
+ "alwayson_scripts": {
13
+ "Vectorscope CC": {
14
+ "args": [
15
+ true,
16
+ true,
17
+ -2.5,
18
+ 0.5,
19
+ 1.25,
20
+ 0.0,
21
+ 0.0,
22
+ 0.0,
23
+ false,
24
+ "Straight Abs.",
25
+ "Flat"
26
+ ]
27
+ }
28
+ }
29
+ }
sd-webui-vectorscope-cc/scripts/Vectorscope.png ADDED
sd-webui-vectorscope-cc/scripts/__pycache__/cc.cpython-310.pyc ADDED
Binary file (10.1 kB). View file
 
sd-webui-vectorscope-cc/scripts/__pycache__/cc_colorpicker.cpython-310.pyc ADDED
Binary file (1.3 kB). View file
 
sd-webui-vectorscope-cc/scripts/__pycache__/cc_const.cpython-310.pyc ADDED
Binary file (724 Bytes). View file
 
sd-webui-vectorscope-cc/scripts/__pycache__/cc_hdr.cpython-310.pyc ADDED
Binary file (3.66 kB). View file
 
sd-webui-vectorscope-cc/scripts/__pycache__/cc_noise.cpython-310.pyc ADDED
Binary file (1.85 kB). View file
 
sd-webui-vectorscope-cc/scripts/__pycache__/cc_scaling.cpython-310.pyc ADDED
Binary file (801 Bytes). View file
 
sd-webui-vectorscope-cc/scripts/__pycache__/cc_settings.cpython-310.pyc ADDED
Binary file (533 Bytes). View file
 
sd-webui-vectorscope-cc/scripts/__pycache__/cc_style.cpython-310.pyc ADDED
Binary file (2.82 kB). View file
 
sd-webui-vectorscope-cc/scripts/__pycache__/cc_version.cpython-310.pyc ADDED
Binary file (1.06 kB). View file
 
sd-webui-vectorscope-cc/scripts/__pycache__/cc_xyz.cpython-310.pyc ADDED
Binary file (2.12 kB). View file
 
sd-webui-vectorscope-cc/scripts/cc.py ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from modules.sd_samplers_kdiffusion import KDiffusionSampler
2
+ import modules.scripts as scripts
3
+ import scripts.cc_const as const
4
+ from modules import shared
5
+ import gradio as gr
6
+ import random
7
+
8
+ from scripts.cc_noise import *
9
+
10
+ from scripts.cc_xyz import xyz_support
11
+
12
+ from scripts.cc_scaling import apply_scaling
13
+
14
+ from scripts.cc_version import VERSION
15
+
16
+ from scripts.cc_colorpicker import create_colorpicker
17
+ from scripts.cc_colorpicker import horizontal_js
18
+ from scripts.cc_colorpicker import vertical_js
19
+
20
+ from scripts.cc_style import StyleManager
21
+ style_manager = StyleManager()
22
+ style_manager.load_styles()
23
+
24
+ og_callback = KDiffusionSampler.callback_state
25
+
26
+ class VectorscopeCC(scripts.Script):
27
+ def __init__(self):
28
+ self.xyzCache = {}
29
+ xyz_support(self.xyzCache)
30
+
31
+ def title(self):
32
+ return "Vectorscope CC"
33
+
34
+ def show(self, is_img2img):
35
+ return scripts.AlwaysVisible
36
+
37
+ def ui(self, is_img2img):
38
+
39
+ with gr.Accordion(f"Vectorscope CC {VERSION}", elem_id='vec-cc-' + ('img' if is_img2img else 'txt'), open=False):
40
+
41
+ with gr.Row():
42
+ enable = gr.Checkbox(label="Enable")
43
+ latent = gr.Checkbox(label="Alt. (Stronger Effects)")
44
+
45
+ with gr.Row():
46
+ bri = gr.Slider(label="Brightness", minimum=const.Brightness.minimum, maximum=const.Brightness.maximum, step=0.1, value=const.Brightness.default)
47
+ con = gr.Slider(label="Contrast", minimum=const.Contrast.minimum, maximum=const.Contrast.maximum, step=0.05, value=const.Contrast.default)
48
+ sat = gr.Slider(label="Saturation", minimum=const.Saturation.minimum, maximum=const.Saturation.maximum, step=0.05, value=const.Saturation.default)
49
+
50
+ with gr.Row():
51
+ with gr.Column():
52
+ r = gr.Slider(label="R", info='Cyan | Red', minimum=const.R.minimum, maximum=const.R.maximum, step=0.05, value=const.R.default, elem_id='cc-r-' + ('img' if is_img2img else 'txt'))
53
+ g = gr.Slider(label="G", info='Magenta | Green',minimum=const.G.minimum, maximum=const.G.maximum, step=0.05, value=const.G.default, elem_id='cc-g-' + ('img' if is_img2img else 'txt'))
54
+ b = gr.Slider(label="B", info='Yellow | Blue',minimum=const.B.minimum, maximum=const.B.maximum, step=0.05, value=const.B.default, elem_id='cc-b-' + ('img' if is_img2img else 'txt'))
55
+
56
+ create_colorpicker(is_img2img)
57
+
58
+ for component in [r, g, b]:
59
+ component.change(None, inputs=[r, g, b], outputs=[], _js=horizontal_js(is_img2img))
60
+ component.change(None, inputs=[r, g, b], outputs=[], _js=vertical_js(is_img2img))
61
+
62
+ with gr.Accordion("Styles", open=False):
63
+
64
+ with gr.Row():
65
+ style_choice = gr.Dropdown(label="Styles", choices=style_manager.list_style(), scale = 3)
66
+ apply_btn = gr.Button(value="Apply Style", elem_id='cc-apply-' + ('img' if is_img2img else 'txt'), scale = 2)
67
+ refresh_btn = gr.Button(value="Refresh Style", scale = 2)
68
+ with gr.Row():
69
+ style_name = gr.Textbox(label="Style Name", scale = 3)
70
+ save_btn = gr.Button(value="Save Style", elem_id='cc-save-' + ('img' if is_img2img else 'txt'), scale = 2)
71
+ delete_btn = gr.Button(value="Delete Style", scale = 2)
72
+
73
+ apply_btn.click(fn=style_manager.get_style, inputs=style_choice, outputs=[latent, bri, con, sat, r, g, b])
74
+ save_btn.click(fn=lambda *args: gr.update(choices=style_manager.save_style(*args)), inputs=[style_name, latent, bri, con, sat, r, g, b], outputs=style_choice)
75
+ delete_btn.click(fn=lambda name: gr.update(choices=style_manager.delete_style(name)), inputs=style_name, outputs=style_choice)
76
+ refresh_btn.click(fn=lambda _: gr.update(choices=style_manager.list_style()), outputs=style_choice)
77
+
78
+ with gr.Accordion("Advanced Settings", open=False):
79
+ doHR = gr.Checkbox(label="Process Hires. fix")
80
+ method = gr.Radio(["Straight", "Straight Abs.", "Cross", "Cross Abs.", "Ones", "N.Random", "U.Random", "Multi-Res", "Multi-Res Abs."], label="Noise Settings", value="Straight Abs.")
81
+ scaling = gr.Radio(["Flat", "Cos", "Sin", "1 - Cos", "1 - Sin"], label="Scaling Settings", value="Flat")
82
+
83
+ with gr.Row():
84
+ reset_btn = gr.Button(value="Reset")
85
+ self.register_reset(reset_btn, enable, latent, bri, con, sat, r, g, b, doHR, method, scaling)
86
+
87
+ random_btn = gr.Button(value="Randomize")
88
+ self.register_random(random_btn, bri, con, sat, r, g, b)
89
+
90
+ self.infotext_fields = []
91
+ self.paste_field_names = []
92
+ self.infotext_fields = [
93
+ (enable, lambda d: enable.update(value=("Vec CC Enabled" in d))),
94
+ (latent, "Vec CC Alt"),
95
+ (bri, "Vec CC Brightness"),
96
+ (con, "Vec CC Contrast"),
97
+ (sat, "Vec CC Saturation"),
98
+ (r, "Vec CC R"),
99
+ (g, "Vec CC G"),
100
+ (b, "Vec CC B"),
101
+ (method, "Vec CC Noise"),
102
+ (doHR, "Vec CC Proc HrF"),
103
+ (scaling, "Vec CC Scaling"),
104
+ ]
105
+
106
+ for _, name in self.infotext_fields:
107
+ self.paste_field_names.append(name)
108
+
109
+ return [enable, latent, bri, con, sat, r, g, b, doHR, method, scaling]
110
+
111
+ def register_reset(self, reset_btn, enable, latent, bri, con, sat, r, g, b, doHR, method, scaling):
112
+ for component in [enable, latent, doHR]:
113
+ reset_btn.click(fn=lambda _: gr.update(value=False), outputs=component)
114
+ for component in [bri, con, r, g, b]:
115
+ reset_btn.click(fn=lambda _: gr.update(value=0.0), outputs=component)
116
+ for component in [sat]:
117
+ reset_btn.click(fn=lambda _: gr.update(value=const.Saturation.default), outputs=component)
118
+
119
+ reset_btn.click(fn=lambda _: gr.update(value='Straight Abs.'), outputs=method)
120
+ reset_btn.click(fn=lambda _: gr.update(value='Flat'), outputs=scaling)
121
+
122
+ def register_random(self, random_btn, bri, con, sat, r, g, b):
123
+ for component in [bri, con, r, g, b]:
124
+ random_btn.click(fn=lambda _: gr.update(value=round(random.uniform(const.R.minimum, const.R.maximum), 2)), outputs=component)
125
+ for component in [sat]:
126
+ random_btn.click(fn=lambda _: gr.update(value=round(random.uniform(const.Saturation.minimum, const.Saturation.maximum), 2)), outputs=component)
127
+
128
+ def parse_bool(self, string:str):
129
+ if string.lower() == "true":
130
+ return True
131
+ if string.lower() == "false":
132
+ return False
133
+
134
+ raise ValueError(f"Invalid Value: {string}")
135
+
136
+ def process(self, p, enable:bool, latent:bool, bri:float, con:float, sat:float, r:float, g:float, b:float, doHR:bool, method:str, scaling:str):
137
+ KDiffusionSampler.isHR_Pass = False
138
+ if 'Enable' in self.xyzCache.keys():
139
+ enable = self.parse_bool(self.xyzCache['Enable'])
140
+
141
+ if not enable:
142
+ if 'Enable' not in self.xyzCache.keys():
143
+ if len(self.xyzCache) > 0:
144
+ print('\n[X/Y/Z Plot] x [Vec.CC] Extension is not Enabled!\n')
145
+ self.xyzCache.clear()
146
+
147
+ KDiffusionSampler.callback_state = og_callback
148
+ return p
149
+
150
+ if 'Random' in self.xyzCache.keys():
151
+ if len(self.xyzCache) > 1:
152
+ print('\n[X/Y/Z Plot] x [Vec.CC] Randomize is Enabled!\nSome settings will not apply!\n')
153
+ else:
154
+ print('\n[X/Y/Z Plot] x [Vec.CC] Randomize is Enabled!\n')
155
+
156
+ cc_seed = None
157
+
158
+ for k, v in self.xyzCache.items():
159
+ match k:
160
+ case 'Alt':
161
+ latent = self.parse_bool(v)
162
+ case 'Brightness':
163
+ bri = v
164
+ case 'Contrast':
165
+ con = v
166
+ case 'Saturation':
167
+ sat = v
168
+ case 'R':
169
+ r = v
170
+ case 'G':
171
+ g = v
172
+ case 'B':
173
+ b = v
174
+ case 'DoHR':
175
+ doHR = self.parse_bool(v)
176
+ case 'Method':
177
+ method = v
178
+ case 'Scaling':
179
+ scaling = v
180
+ case 'Random':
181
+ cc_seed = v
182
+
183
+ self.xyzCache.clear()
184
+
185
+ if method == 'Disabled':
186
+ KDiffusionSampler.callback_state = og_callback
187
+ return p
188
+
189
+ steps = p.steps
190
+ if not hasattr(p, 'enable_hr') and hasattr(p, 'denoising_strength') and not shared.opts.img2img_fix_steps:
191
+ steps = int(steps * p.denoising_strength)
192
+
193
+ if cc_seed is not None:
194
+ random.seed(cc_seed)
195
+
196
+ bri = round(random.uniform(const.Contrast.minimum, const.Contrast.maximum), 2)
197
+ con = round(random.uniform(const.Contrast.minimum, const.Contrast.maximum), 2)
198
+ r = round(random.uniform(const.R.minimum, const.R.maximum), 2)
199
+ g = round(random.uniform(const.G.minimum, const.G.maximum), 2)
200
+ b = round(random.uniform(const.B.minimum, const.B.maximum), 2)
201
+
202
+ sat = round(random.uniform(const.Saturation.minimum, const.Saturation.maximum), 2)
203
+
204
+ print(f'-> Seed: {cc_seed}')
205
+ print(f'Brightness:\t{bri}')
206
+ print(f'Contrast:\t{con}')
207
+ print(f'Saturation:\t{sat}')
208
+ print(f'R:\t\t{r}')
209
+ print(f'G:\t\t{g}')
210
+ print(f'B:\t\t{b}\n')
211
+
212
+ if hasattr(shared.opts, 'cc_metadata') and shared.opts.cc_metadata is True:
213
+ p.extra_generation_params['Vec CC Enabled'] = enable
214
+ p.extra_generation_params['Vec CC Alt'] = latent
215
+ p.extra_generation_params['Vec CC Brightness'] = bri
216
+ p.extra_generation_params['Vec CC Contrast'] = con
217
+ p.extra_generation_params['Vec CC Saturation'] = sat
218
+ p.extra_generation_params['Vec CC R'] = r
219
+ p.extra_generation_params['Vec CC G'] = g
220
+ p.extra_generation_params['Vec CC B'] = b
221
+ p.extra_generation_params['Vec CC Noise'] = method
222
+ p.extra_generation_params['Vec CC Proc HrF'] = doHR
223
+ p.extra_generation_params['Vec CC Scaling'] = scaling
224
+ p.extra_generation_params['Vec CC Version'] = VERSION
225
+
226
+ bri /= steps
227
+ con /= steps
228
+ sat = pow(sat, 1.0 / steps)
229
+ r /= steps
230
+ g /= steps
231
+ b /= steps
232
+
233
+ mode = 'x' if latent else 'denoised'
234
+
235
+ # Channel 0: Dark | Bright
236
+ # Channel 1: Purple | Green
237
+ # Channel 2: Red | Cyan
238
+ # Channel 2: Violet | Yellow
239
+
240
+ def callback_state(self, d):
241
+ if not doHR and self.isHR_Pass is True:
242
+ return og_callback(self, d)
243
+
244
+ source = d[mode]
245
+
246
+ # "Straight", "Straight Abs.", "Cross", "Cross Abs.", "Ones", "N.Random", "U.Random", "Multi-Res", "Multi-Res Abs."
247
+ if 'Straight' in method:
248
+ target = d[mode]
249
+ elif 'Cross' in method:
250
+ cross = 'x' if mode == 'denoised' else 'denoised'
251
+ target = d[cross]
252
+ elif 'Multi-Res' in method:
253
+ target = multires_noise(d[mode], 'Abs' in method)
254
+ elif method == 'Ones':
255
+ target = ones(d[mode])
256
+ elif method == 'N.Random':
257
+ target = normal_noise(d[mode])
258
+ elif method == 'U.Random':
259
+ target = gaussian_noise(d[mode])
260
+
261
+ if 'Abs' in method:
262
+ target = to_abs(target)
263
+
264
+ batchSize = d[mode].size(0)
265
+
266
+ mods = apply_scaling(scaling, d["i"], steps, bri, con, sat, r, g, b)
267
+
268
+ for i in range(batchSize):
269
+ BRIGHTNESS = [source[i, 0], target[i, 0]]
270
+ R = [source[i, 2], target[i, 2]]
271
+ G = [source[i, 1], target[i, 1]]
272
+ B = [source[i, 3], target[i, 3]]
273
+
274
+ BRIGHTNESS[0] += BRIGHTNESS[1] * mods[0]
275
+ BRIGHTNESS[0] += get_delta(BRIGHTNESS[0]) * mods[1]
276
+
277
+ R[0] -= R[1] * mods[3]
278
+ G[0] += G[1] * mods[4]
279
+ B[0] -= B[1] * mods[5]
280
+
281
+ R[0] *= mods[2]
282
+ G[0] *= mods[2]
283
+ B[0] *= mods[2]
284
+
285
+ return og_callback(self, d)
286
+
287
+ KDiffusionSampler.callback_state = callback_state
288
+ return p
289
+
290
+ def before_hr(self, p, *args):
291
+ KDiffusionSampler.isHR_Pass = True
292
+
293
+ def postprocess(self, p, processed, *args):
294
+ KDiffusionSampler.callback_state = og_callback
sd-webui-vectorscope-cc/scripts/cc_colorpicker.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import modules.scripts as scripts
2
+ import gradio as gr
3
+
4
+ WHEEL = scripts.basedir() + '/scripts/Vectorscope.png'
5
+ DOT = scripts.basedir() + '/scripts/dot.png'
6
+
7
+ def create_colorpicker(is_img):
8
+ gr.Image(WHEEL, type='filepath', interactive=False, container=False, elem_id='cc-colorwheel-' + ('img' if is_img else 'txt'))
9
+ gr.Image(DOT, type='filepath', interactive=False, container=False, elem_id='cc-temp-' + ('img' if is_img else 'txt'))
10
+
11
+ def horizontal_js(is_img):
12
+ mag = '(Math.abs(r) + Math.abs(g) + Math.abs(b))'
13
+ calc = '25 * Math.sqrt(r*r+g*g+b*b) * (r * -0.5 + g * -0.5 + b * 1.0) / ' + mag
14
+ cond = '(' + mag + ' === 0 ? 0 : ' + calc + ')'
15
+
16
+ return "(r, g, b) => {document.getElementById('cc-dot-" + ('img' if is_img else 'txt') + "').style.left = 'calc(50% + ' +(" + cond + "- 12) + 'px)'}"
17
+
18
+ def vertical_js(is_img):
19
+ mag = '(Math.abs(r) + Math.abs(g) + Math.abs(b))'
20
+ calc = '25 * Math.sqrt(r*r+g*g+b*b) * (r * -0.866 + g * 0.866 + b * 0.0) / ' + mag
21
+ cond = '(' + mag + ' === 0 ? 0 : ' + calc + ')'
22
+
23
+ return "(r, g, b) => {document.getElementById('cc-dot-" + ('img' if is_img else 'txt') + "').style.top = 'calc(50% + ' +(" + cond + "- 12) + 'px)'}"
sd-webui-vectorscope-cc/scripts/cc_const.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class Param():
2
+ def __init__(self, minimum, maximum, default):
3
+ self.minimum = minimum
4
+ self.maximum = maximum
5
+ self.default = default
6
+
7
+ Brightness = Param(-7.5, 7.5, 0.0)
8
+ Contrast = Param(-5.0, 5.0, 0.0)
9
+ Saturation = Param(0.15, 1.85, 1.0)
10
+ R = Param(-4.0, 4.0, 0.0)
11
+ G = Param(-4.0, 4.0, 0.0)
12
+ B = Param(-4.0, 4.0, 0.0)
sd-webui-vectorscope-cc/scripts/cc_hdr.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import modules.scripts as scripts
2
+ import gradio as gr
3
+ import numpy as np
4
+ import cv2 as cv
5
+
6
+ from modules.processing import process_images, get_fixed_seed
7
+ from copy import copy
8
+
9
+ # https://docs.opencv.org/4.8.0/d2/df0/tutorial_py_hdr.html
10
+ def merge_HDR(imgs:list, path:str, depth:str, fmt:str, gamma:float):
11
+ import datetime
12
+ import math
13
+ import os
14
+
15
+ output_folder = os.path.join(path, 'hdr')
16
+ if not os.path.exists(output_folder):
17
+ os.makedirs(output_folder)
18
+
19
+ imgs_np = [np.array(img, dtype=np.uint8) for img in imgs]
20
+
21
+ merge = cv.createMergeMertens()
22
+ hdr = merge.process(imgs_np)
23
+ hdr += math.ceil(0 - np.min(hdr) * 1000) / 1000
24
+
25
+ #print(f'{np.min(hdr)}, {np.max(hdr)}')
26
+
27
+ target = (65535 if depth == '16bpc' else 255)
28
+ precision = ('uint16' if depth == '16bpc' else 'uint8')
29
+
30
+ hdr = np.power(hdr, (1 / gamma))
31
+
32
+ ldr = np.clip(hdr * target, 0, target).astype(precision)
33
+ rgb = cv.cvtColor(ldr, cv.COLOR_BGR2RGB)
34
+
35
+ cv.imwrite(os.path.join(output_folder, f'{datetime.datetime.now().strftime("%H-%M-%S")}{fmt}'), rgb)
36
+
37
+ class VectorHDR(scripts.Script):
38
+
39
+ def title(self):
40
+ return "High Dynamic Range"
41
+
42
+ def show(self, is_img2img):
43
+ return True
44
+
45
+ def ui(self, is_img2img):
46
+ with gr.Row():
47
+ count = gr.Slider(label="Brackets", minimum=3, maximum=9, step=2, value=7)
48
+ gap = gr.Slider(label="Gaps", minimum=0.50, maximum=2.50, step=0.25, value=1.50)
49
+
50
+ with gr.Accordion("Merge Options", elem_id='vec-hdr-' + ('img' if is_img2img else 'txt'), open=False):
51
+ auto = gr.Checkbox(label="Automatically Merge", value=True)
52
+
53
+ with gr.Row():
54
+ depth = gr.Radio(['16bpc', '8bpc'], label="Bit Depth", value="16bpc")
55
+ fmt = gr.Radio(['.tiff', '.png'], label="Image Format", value=".tiff")
56
+
57
+ gamma = gr.Slider(label="Gamma", info='Lower: Darker | Higher: Brighter',minimum=0.2, maximum=2.2, step=0.2, value=1.2)
58
+
59
+ return [count, gap, auto, depth, fmt, gamma]
60
+
61
+ def run(self, p, count:int, gap:float, auto:bool, depth:str, fmt:str, gamma:float):
62
+ center = count // 2
63
+
64
+ p.seed = get_fixed_seed(p.seed)
65
+ p.scripts.script('vectorscope cc').xyzCache.update({
66
+ 'Enable':'True',
67
+ 'Alt':'True',
68
+ 'Brightness': 0,
69
+ 'DoHR': 'False',
70
+ 'Method': 'Ones',
71
+ 'Scaling': '1 - Cos'
72
+ })
73
+
74
+ baseline = process_images(p)
75
+ pc = copy(p)
76
+
77
+ imgs = [None] * count
78
+ imgs[center] = baseline.images[0]
79
+
80
+ brackets = brightness_brackets(count, gap)
81
+
82
+ for it in range(count):
83
+ if it == center:
84
+ continue
85
+
86
+ pc.scripts.script('vectorscope cc').xyzCache.update({
87
+ 'Enable':'True',
88
+ 'Alt':'True',
89
+ 'Brightness': brackets[it],
90
+ 'DoHR': 'False',
91
+ 'Method': 'Ones',
92
+ 'Scaling': '1 - Cos'
93
+ })
94
+
95
+ proc = process_images(pc)
96
+ imgs[it] = proc.images[0]
97
+
98
+ if not auto:
99
+ baseline.images = imgs
100
+ return baseline
101
+ else:
102
+ merge_HDR(imgs, p.outpath_samples, depth, fmt, gamma)
103
+ return baseline
104
+
105
+ def brightness_brackets(count, gap):
106
+ half = count // 2
107
+ return [gap * (i - half) for i in range(count)]
sd-webui-vectorscope-cc/scripts/cc_noise.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from modules import devices
2
+ import torch
3
+
4
+ def get_delta(latent):
5
+ mean = torch.mean(latent)
6
+ return torch.sub(latent, mean)
7
+
8
+ def to_abs(latent):
9
+ return torch.abs(latent)
10
+
11
+ def zeros(latent):
12
+ return torch.zeros_like(latent)
13
+
14
+ def ones(latent):
15
+ return torch.ones_like(latent)
16
+
17
+ def gaussian_noise(latent):
18
+ return torch.rand_like(latent)
19
+
20
+ def normal_noise(latent):
21
+ return torch.randn_like(latent)
22
+
23
+ def multires_noise(latent, use_zero:bool, iterations=8, discount=0.4):
24
+ """
25
+ Reference: https://wandb.ai/johnowhitaker/multires_noise/reports/Multi-Resolution-Noise-for-Diffusion-Model-Training--VmlldzozNjYyOTU2
26
+ Credit: Kohya_SS
27
+ """
28
+ noise = zeros(latent) if use_zero else ones(latent)
29
+
30
+ batchSize = noise.size(0)
31
+ height = noise.size(2)
32
+ width = noise.size(3)
33
+
34
+ device = devices.get_optimal_device()
35
+ upsampler = torch.nn.Upsample(size=(height, width), mode="bilinear").to(device)
36
+
37
+ for b in range(batchSize):
38
+ for i in range(iterations):
39
+ r = torch.rand(1).item() * 2 + 2
40
+
41
+ wn = max(1, int(width / (r**i)))
42
+ hn = max(1, int(height / (r**i)))
43
+
44
+ for c in range(4):
45
+ noise[b, c] += upsampler(torch.randn(1, 1, hn, wn).to(device))[0, 0] * discount**i
46
+
47
+ if wn == 1 or hn == 1:
48
+ break
49
+
50
+ return noise / noise.std()
sd-webui-vectorscope-cc/scripts/cc_scaling.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from math import cos
2
+ from math import sin
3
+ from math import pi
4
+
5
+ def apply_scaling(alg:str, current_step:int, total_steps:int, bri:float, con:float, sat:float, r:float, g:float, b:float):
6
+ ratio = float(current_step / total_steps)
7
+ rad = ratio * pi / 2
8
+
9
+ mod = 1.0
10
+
11
+ match alg:
12
+ case "Cos":
13
+ mod = cos(rad)
14
+ case "Sin":
15
+ mod = sin(rad)
16
+ case "1 - Cos":
17
+ mod = (1 - cos(rad))
18
+ case "1 - Sin":
19
+ mod = (1 - sin(rad))
20
+
21
+ return [
22
+ bri * mod,
23
+ con * mod,
24
+ (sat - 1) * mod + 1,
25
+ r * mod,
26
+ g * mod,
27
+ b * mod
28
+ ]
sd-webui-vectorscope-cc/scripts/cc_settings.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ from modules import script_callbacks, shared
2
+
3
+ def on_ui_settings():
4
+ shared.opts.add_option("cc_metadata", shared.OptionInfo(False, "Add Vectorscope CC parameters to generation information", section=("infotext", "Infotext")))
5
+
6
+ script_callbacks.on_ui_settings(on_ui_settings)
sd-webui-vectorscope-cc/scripts/cc_style.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import modules.scripts as scripts
2
+ import scripts.cc_const as const
3
+ import json
4
+
5
+ STYLE_FILE = scripts.basedir() + '/' + 'styles.json'
6
+
7
+ EMPTY_STYLE = {
8
+ 'styles' : {},
9
+ 'deleted' : {}
10
+ }
11
+
12
+ class StyleManager():
13
+ def __init__(self):
14
+ self.STYLE_SHEET = None
15
+
16
+ def load_styles(self):
17
+ if self.STYLE_SHEET is not None:
18
+ return
19
+
20
+ try:
21
+ with open(STYLE_FILE, 'r') as json_file:
22
+ self.STYLE_SHEET = json.loads(json_file.read())
23
+ print('[Vec. CC] Style Sheet Loaded...')
24
+ except IOError:
25
+ with open(STYLE_FILE, 'w+') as json_file:
26
+ self.STYLE_SHEET = EMPTY_STYLE
27
+ json_file.write(json.dumps(self.STYLE_SHEET))
28
+ print('[Vec. CC] Creating Empty Style Sheet...')
29
+
30
+ def list_style(self):
31
+ return list(self.STYLE_SHEET['styles'].keys())
32
+
33
+ def get_style(self, style_name):
34
+ try:
35
+ style = self.STYLE_SHEET['styles'][style_name]
36
+ return style['alt'], style['brightness'], style['contrast'], style['saturation'], style['rgb'][0], style['rgb'][1], style['rgb'][2]
37
+ except KeyError:
38
+ print(f'\n[Warning] No Style of Name "{style_name}" Found!\n')
39
+ return False, const.Brightness.default, const.Contrast.default, const.Saturation.default, const.R.default, const.G.default, const.B.default
40
+
41
+ def save_style(self, style_name, latent, bri, con, sat, r, g, b):
42
+ if style_name in self.STYLE_SHEET['styles'].keys():
43
+ print(f'\n[Warning] Duplicated Style Name "{style_name}" Detected! Values are not saved!\n')
44
+ return self.list_style()
45
+
46
+ style = {
47
+ 'alt' : latent,
48
+ 'brightness' : bri,
49
+ 'contrast' : con,
50
+ 'saturation' : sat,
51
+ 'rgb' : [r, g, b]
52
+ }
53
+
54
+ self.STYLE_SHEET['styles'].update({style_name:style})
55
+
56
+ with open(STYLE_FILE, 'w+') as json_file:
57
+ json_file.write(json.dumps(self.STYLE_SHEET))
58
+
59
+ print(f'\nStyle of Name "{style_name}" Saved!\n')
60
+ return self.list_style()
61
+
62
+ def delete_style(self, style_name):
63
+ try:
64
+ style = self.STYLE_SHEET['styles'][style_name]
65
+ del self.STYLE_SHEET['styles'][style_name]
66
+ except KeyError:
67
+ print(f'\n[Warning] No Style of Name "{style_name}" Found!\n')
68
+ return self.list_style()
69
+
70
+ self.STYLE_SHEET['deleted'].update({style_name:style})
71
+
72
+ with open(STYLE_FILE, 'w+') as json_file:
73
+ json_file.write(json.dumps(self.STYLE_SHEET))
74
+
75
+ print(f'\nStyle of Name "{style_name}" Deleted!\n')
76
+ return self.list_style()
sd-webui-vectorscope-cc/scripts/cc_version.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from modules import script_callbacks
2
+ import modules.scripts as scripts
3
+ import json
4
+
5
+ VERSION = 'v1.4.10'
6
+
7
+ def clean_outdated(EXT_NAME:str):
8
+ with open(scripts.basedir() + '/' + 'ui-config.json', 'r') as json_file:
9
+ configs = json.loads(json_file.read())
10
+
11
+ cleaned_configs = {key: value for key, value in configs.items() if EXT_NAME not in key}
12
+
13
+ with open(scripts.basedir() + '/' + 'ui-config.json', 'w') as json_file:
14
+ json.dump(cleaned_configs, json_file)
15
+
16
+ def refresh_sliders():
17
+ clean_outdated('cc.py')
18
+ clean_outdated('cc_hdr.py')
19
+
20
+ script_callbacks.on_before_ui(refresh_sliders)
sd-webui-vectorscope-cc/scripts/cc_xyz.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import modules.scripts as scripts
2
+
3
+ def grid_reference():
4
+ for data in scripts.scripts_data:
5
+ if data.script_class.__module__ == 'xyz_grid.py' and hasattr(data, "module"):
6
+ return data.module
7
+
8
+ def xyz_support(cache):
9
+ def apply_field(field):
10
+ def _(p, x, xs):
11
+ cache.update({field : x})
12
+ return _
13
+
14
+ def choices_bool():
15
+ return ["False", "True"]
16
+
17
+ def choices_method():
18
+ return ["Disabled", "Straight", "Straight Abs.", "Cross", "Cross Abs.", "Ones", "N.Random", "U.Random", "Multi-Res", "Multi-Res Abs."]
19
+
20
+ def choices_scaling():
21
+ return ["Flat", "Cos", "Sin", "1 - Cos", "1 - Sin"]
22
+
23
+ xyz_grid = grid_reference()
24
+
25
+ extra_axis_options = [
26
+ xyz_grid.AxisOption("[Vec.CC] Enable", str, apply_field("Enable"), choices=choices_bool),
27
+ xyz_grid.AxisOption("[Vec.CC] Alt.", str, apply_field("Alt"), choices=choices_bool),
28
+ xyz_grid.AxisOption("[Vec.CC] Brightness", float, apply_field("Brightness")),
29
+ xyz_grid.AxisOption("[Vec.CC] Contrast", float, apply_field("Contrast")),
30
+ xyz_grid.AxisOption("[Vec.CC] Saturation", float, apply_field("Saturation")),
31
+ xyz_grid.AxisOption("[Vec.CC] R", float, apply_field("R")),
32
+ xyz_grid.AxisOption("[Vec.CC] G", float, apply_field("G")),
33
+ xyz_grid.AxisOption("[Vec.CC] B", float, apply_field("B")),
34
+ xyz_grid.AxisOption("[Adv.CC] Proc. H.Fix", str, apply_field("DoHR"), choices=choices_bool),
35
+ xyz_grid.AxisOption("[Adv.CC] Method", str, apply_field("Method"), choices=choices_method),
36
+ xyz_grid.AxisOption("[Adv.CC] Scaling", str, apply_field("Scaling"), choices=choices_scaling),
37
+ xyz_grid.AxisOption("[Adv.CC] Randomize", int, apply_field("Random"))
38
+ ]
39
+
40
+ xyz_grid.axis_options.extend(extra_axis_options)
sd-webui-vectorscope-cc/scripts/dot.png ADDED
sd-webui-vectorscope-cc/style.css ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #cc-dot-txt {
2
+ position: absolute;
3
+ width: 24px;
4
+ height: 24px;
5
+ pointer-events: none;
6
+ }
7
+
8
+ #cc-dot-img {
9
+ position: absolute;
10
+ width: 24px;
11
+ height: 24px;
12
+ pointer-events: none;
13
+ }
14
+
15
+ #cc-img-txt {
16
+ cursor: pointer;
17
+ }
18
+
19
+ #cc-img-img {
20
+ cursor: pointer;
21
+ }
22
+
23
+ #vec-cc-txt button {
24
+ border-radius: 0.5em;
25
+ }
26
+
27
+ #vec-cc-txt label {
28
+ border-radius: 0.5em;
29
+ }
30
+
31
+ #vec-cc-img button {
32
+ border-radius: 0.5em;
33
+ }
34
+
35
+ #vec-cc-img label {
36
+ border-radius: 0.5em;
37
+ }
sd-webui-vectorscope-cc/styles.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"styles": {}, "deleted": {}}