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

Conflict With Dynamic Prompt (Wildcards manager tab) #50

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ This extension is an extension of the built-in Composable Diffusion.
This allows you to determine the region of the latent space that reflects your subprompts.

## How to use
![20230303.png](./screenshots/20230303.png)
![20230213.png](./screenshots/20230213.png)


### Enabled
The effect of Latent Couple appears only when Enabled is checked.

Expand Down Expand Up @@ -41,11 +43,34 @@ outputs
- end at step=4 https://imgur.com/a1kyvhX
- end at step=0 https://imgur.com/yhGF7g8

## Old prerequisite
This extension need to apply cfg_denoised_callback-ea9bd9fc.patch (as of Feb 5, 2023 origin/HEAD commit ea9bd9fc).

## ~~Prerequisite for prompt pasting~~
## ~~Prerequisite for gradio Image and Sketch component bug fix~~
This fix is no longer suitable for latest webui commit at 22bcc7be, with gradio dependency upgraded to 3.23.

I'll keep the fix here for people still using older versions of webui.

Activate your venv in webui root directory

For Windows, in cmd
```
venv\Scripts\activate.bat
```
git apply --ignore-whitespace extensions/stable-diffusion-webui-two-shot/cfg_denoised_callback-ea9bd9fc.patch
For Linux
```
source venv/bin/activate
```
Then, install wheel distribution with bugfix applied
```
pip install --force-reinstall --no-deps extensions/stable-diffusion-webui-two-shot/gradio-3.16.2-py3-none-any.whl
```
For bugfix related modifications, see https://github.com/ashen-sensored/gradio/tree/3.16.2


## Issues
- ~~The extension's mask color sketching function does not work well with chrome(extreme stuttering) due to gradio's Image component bug.~~ Please keep the browser scaling at 100% while creating blank canvas to avoid the bug.
See prerequisite above. The fix is no longer suitable for latest webui version at 22bcc7be, with gradio dependency upgraded to 3.23.


## Credits
- two shot diffusion.ipynb https://colab.research.google.com/drive/1UdElpQfKFjY5luch9v_LlmSdH7AmeiDe?usp=sharing
83 changes: 0 additions & 83 deletions cfg_denoised_callback-ea9bd9fc.patch

This file was deleted.

Binary file added gradio-3.16.2-py3-none-any.whl
Binary file not shown.
Binary file added screenshots/20230303.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
99 changes: 99 additions & 0 deletions scripts/sketch_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import numpy as np
import cv2
import base64


def count_high_freq_colors(image):
im = image.getcolors(maxcolors=1024*1024)
sorted_colors = sorted(im, key=lambda x: x[0], reverse=True)

freqs = [c[0] for c in sorted_colors]
mean_freq = sum(freqs) / len(freqs)

high_freq_colors = [c for c in sorted_colors if c[0] > max(2, mean_freq*1.25)]
return high_freq_colors

def get_high_freq_colors(image, similarity_threshold=30):
image_copy = image.copy()
high_freq_colors = count_high_freq_colors(image)
# Check for similar colors and replace the lower frequency color with the higher frequency color in the image
for i, (freq1, color1) in enumerate(high_freq_colors):
for j, (freq2, color2) in enumerate(high_freq_colors):
if (color_distance(color1, color2) < similarity_threshold) or (color_distance(color1, opaque_color_on_white(color2, 0.5)) < 5):
if(freq2 > freq1):
replace_color(image_copy, color1, color2)

high_freq_colors = count_high_freq_colors(image_copy)
print(high_freq_colors)
return [high_freq_colors, image_copy]

def color_quantization(image, color_frequency_list):
# Convert the color frequency list to a set of unique colors
unique_colors = set([color for _, color in color_frequency_list])

# Create a mask for the image with True where the color is in the unique colors set
mask = np.any(np.all(image.reshape(-1, 1, 3) == np.array(list(unique_colors)), axis=2), axis=1).reshape(image.shape[:2])

# Create a new image with all pixels set to white
new_image = np.full_like(image, 255)

# Copy the pixels from the original image that have a color in the color frequency list
new_image[mask] = image[mask]
return new_image


def create_binary_mask(img_arr, target_color):
# Create mask of pixels with target color
mask = np.all(img_arr == target_color, axis=-1)

# Convert mask to binary matrix
binary_matrix = mask.astype(int)

return binary_matrix


def create_binary_matrix_base64(img_arr, target_color):
# Create mask of pixels with target color
mask = np.all(img_arr == target_color, axis=-1)

# Convert mask to binary matrix
binary_matrix = mask.astype(int)
from datetime import datetime
binary_file_name = f'mask-{datetime.now().timestamp()}.png'
_, im_arr = cv2.imencode('.png', binary_matrix * 255)

im_bytes = im_arr.tobytes()
im_b64 = base64.b64encode(im_bytes).decode('ascii')
# binary_matrix = torch.from_numpy(binary_matrix).unsqueeze(0).unsqueeze(0)
return mask, im_b64

def create_binary_matrix_img(img_arr, target_color):
# Create mask of pixels with target color
mask = np.all(img_arr == target_color, axis=-1)

# Convert mask to binary matrix
binary_matrix = mask.astype(int)
from datetime import datetime
binary_file_name = f'mask-{datetime.now().timestamp()}.png'
cv2.imwrite(binary_file_name, binary_matrix * 255)

#binary_matrix = torch.from_numpy(binary_matrix).unsqueeze(0).unsqueeze(0)
return binary_file_name

def color_distance(color1, color2):
return sum((a - b) ** 2 for a, b in zip(color1, color2)) ** 0.5

def replace_color(image, old_color, new_color):
pixels = image.load()
width, height = image.size
for x in range(width):
for y in range(height):
if pixels[x, y] == old_color:
pixels[x, y] = new_color

def opaque_color_on_white(color, a):
r, g, b = color
opaque_red = int((1 - a) * 255 + a * r)
opaque_green = int((1 - a) * 255 + a * g)
opaque_blue = int((1 - a) * 255 + a * b)
return (opaque_red, opaque_green, opaque_blue)
Loading