-
-
Notifications
You must be signed in to change notification settings - Fork 230
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
Regional prompting / Attention couple proof of concept #639
Conversation
If you want to check my full ComfuUI Regional Prompting that inspired this PR : https://www.reddit.com/r/StableDiffusion/comments/1c7eaza/comfyui_easy_regional_prompting_workflow_3/ Note : The AttentionCouple node use a JS trick where you have to right click then use "add input", so a dumped workflow from krita-ai-diffusion is unusable until you connect the correct mask & cond. |
An example for #387 : |
sorry if it s too nooby, but how do i implement this? thanks in advance. |
Improved the coherency by subtracting the upper masks from each control layer mask. This allow to better respect the order of the control layers. This only affect images where there is a covering between two control layers. If you put a control layer below another one covering it entirely, as intended it will have an empty mask and be ignored. It could be an option, but from my experience it works better that way, otherwise smaller zones are frequently ignored in favor of bigger covering ones. I tried manually computing masks intersections to combine prompts, but the difference in result is marginal, and the computation by nodes would be a nightmare. |
That was only when testing my own workflow, where the masks are made programmatically, and sometimes there is a gap of one pixel between two masks. Having a 0.0 mask covering all the regions seems to fill the possible gaps and prevent breakage on some fringe cases. Nothing to do with the krita plugin, where all the masks are correct, I'll get rid of the 0 mask in the plugin and see if there is any problem.
That would be great, but once again that was me thinking about my own workflow ; as it would allow me to get rid of the multiple-of-64 limitation. 😅 For the plugin it makes perfect sense to send an a small as possible mask directly.
Perfect. |
The code is merged. I left the masks at full size for now, removed the 0 str base mask, and got rid of the unnecessary extent scale to a multiple of 64. |
Thanks! Maybe this would be a good point to clean up this PR and merge it into regions branch. |
Yes I think it's in a good state for this. I'll take a look at lint & type checks, [edit] : All done ! |
Can't wait to try this |
There may be a bug when removing regions < 10% coverage. I did a test with many small regions. In those case I got this error and a black image :
The problem goes away if I put the coverage to a very low value in the code. Here is the faulty workflow output : workflow-error.json And the working one : workflow-ok.json It seems that in the first case the leftover mask calculated subtracted a mask not used in the final workflow. I don't know if the problem can be avoided at in |
Got it. The culprit is the # Remove from each region mask any overlapping areas from regions above it.
accumulated_mask = None
for i in range(len(result_regions) - 1, -1, -1):
region, job_region = result_regions[i]
mask = region.mask
if accumulated_mask is None:
accumulated_mask = Image.copy(region.mask)
else:
mask = Image.mask_subtract(mask, accumulated_mask)
(...) As it can be done on a region that will be ignored by the coverage check just below. To be correct, # Remove from each region mask any overlapping areas from regions above it.
accumulated_mask = None
for i in range(len(result_regions) - 1, -1, -1):
region, job_region = result_regions[i]
mask = region.mask
if accumulated_mask is not None:
mask = Image.mask_subtract(mask, accumulated_mask)
coverage = mask.average()
if coverage > 0.9:
# Single region covers (almost) entire image, don't use regional conditioning.
print(f"Using single region {region.positive[:10]}: coverage is {coverage}")
result.control += region.control
return result, [job_region]
elif coverage < 0.1:
# Region has less than 10% coverage, remove it.
print(f"Skipping region {region.positive[:10]}: coverage is {coverage}")
result_regions.pop(i)
else:
# Initialize accumulated_mask if needed
if accumulated_mask is None:
accumulated_mask = Image.copy(region.mask)
# Accumulate mask for next region, and store modified mask.
accumulated_mask = Image.mask_add(accumulated_mask, region.mask)
region.mask = mask |
Still, for those little regions, I would rather use a 5% coverage check. Maybe if we had this as a setting value ? |
Ah nice find, it pays off to do some stress testing... I don't mind setting coverage check to 5% (but don't really want to put it in settings). There is still some code I think we no longer need?
|
IMO in this case it makes more sense to adjust the region mask to better match the result, then the 90% rule should take care of it. Alternatively select the region you want and switch to "Refine Region" (I don't know if it considers the selection at the moment, but that could make sense). In any case I don't think there needs to be a bool passed so far down and branches in the workflow? If you want a single region, only a single region should be passed to the api/workflow. |
Read you response too fast, my bad. Of course painting the region manually should be an acceptable fix. That being said, I perfectly understand if you want to get rid of this option. I can keep it in my custom fork only. 😅 |
My main concern is that the functionality is in the wrong place, it should be much earlier with the rest of the region-selection/filtering process. If you don't want regions, just don't pass them to the workflow in the first place. Also the |
I don't know whether it's already important or related: I wanted to try out the PR, but only have CloudGPU available. Since cgem156-ComfyUI isn't installed in the "Stable Diffusion ComfyUI for Krita", I tried to add it as custom node. That didn't work - should it be possible to add this/a custom node to the ComfyUI instance of the CloudGPU image? |
The last commits doesn't use the cgem156 node anymore, but the one adapted by Acly. You have to manually switch the branch of |
* Attention couple and mask manipulation nodes * Apply attention control layer in generate * Handle attention call in refine method * Subtract lower masks from each attention control layer mask * Use conditioning positive prompt * Prevent pointer creation to first region mask * Handle attention couple via the new Regions system * Improved attention couple usage for hires fix * Find region prompts according to current selection overlap * Check if LoRA already loaded when extracting from prompts * Correctly apply attention conditioning to hires pass * Also apply attention couple to inpaint hires pass. Fix wrong image type error. * Use empty background region by default * Prevent result initialization to overwrite {prompt} token * Merge region positive prompts with root positive prompt in the regions to_api * No more prompt merging in apply_attention. Removed unnecessary negative prompts clip encoding * Using ETN_AttentionCouple and ETN_ListAppend nodes to apply attention couple * Initialize accumulated_mask with the first valid region mask
* Attention couple and mask manipulation nodes * Apply attention control layer in generate * Handle attention call in refine method * Subtract lower masks from each attention control layer mask * Use conditioning positive prompt * Prevent pointer creation to first region mask * Handle attention couple via the new Regions system * Improved attention couple usage for hires fix * Find region prompts according to current selection overlap * Check if LoRA already loaded when extracting from prompts * Correctly apply attention conditioning to hires pass * Also apply attention couple to inpaint hires pass. Fix wrong image type error. * Use empty background region by default * Prevent result initialization to overwrite {prompt} token * Merge region positive prompts with root positive prompt in the regions to_api * No more prompt merging in apply_attention. Removed unnecessary negative prompts clip encoding * Using ETN_AttentionCouple and ETN_ListAppend nodes to apply attention couple * Initialize accumulated_mask with the first valid region mask
Hello,
Using the custom node cgem156-ComfyUI , I was able to have a functional regional prompting workflow for SDXL.
Here is a proof of concept in krita-ai-diffusion, using a new control layer type "Attention", and splitting the prompt in lines and parsing for
ZONE
starting lines (orBREAK
,PROMPT
,ATT
, and an optional number... TBD) and new Paint layer in Krita we are able to greatly influence the rendering.Here is a step by step :
ZONE
then describe the contentZONE
line that will be applied only to the image outside the defined zonesAn example with a single zone :
The second ZONE is automatically affected to the cat prompt. The prompts used as attention couple are : "photography of a dog, two animals, a city street in the background" and "photography of a cat, two animals, a city street in the background".
Another example:
To do :
AttentionCouple
ETN_AttentionCouple
node