You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I just recently became aware of this CR_ComicPanelTemplates node. It seems useful. And the more practical way to make use of something like this is probably either use the output as is or open it in photo editing.
But I am a little fascinated by those generations of comics using models. They are a bit FOCUSED in what they are trained on but the ones that are more general aesthetic models seem cool but not really practical for making something worth while. Here is what I found in the SDXL family of loras.
Vixon's Anime/Manga Styles - manga panels
Vixon's Pony Styles - Soft Panels
Comic page generator | Illustrious Manga Boxes
XL Manga | Comic page generator
Comic Book Page style (Anime + Manga + Western Comics) XL
Manga / Comic Page Templates
But I think the key to that might be to give them a lot of guidance in the form of conditioning from prompt conditioning masks
ConditioningSetMaskAndCombine5 https://github.com/kijai/ComfyUI-KJNodes
as well as controlnet, InstantID, Reactor, and ipAdapters.
as well as using the mask to inpaint generations into the panel.
Anyway this is a long way to say having it output masks for the panels would help in doing this sort of thing.
I tried to do this with subgraphs but it became a pile of spaghetti. Worse it didn't work.
So I did it the proper way.
`class CR_ComicPanelTemplatesMasks:
@classmethod
def INPUT_TYPES(s):
directions = ["left to right", "right to left"]
templates = ["custom",
"G22", "G33",
"H2", "H3",
"H12", "H13",
"H21", "H23",
"H31", "H32",
"V2", "V3",
"V12", "V13",
"V21", "V23",
"V31", "V32"]
return {"required": {
"page_width": ("INT", {"default": 512, "min": 8, "max": 4096}),
"page_height": ("INT", {"default": 512, "min": 8, "max": 4096}),
"template": (templates,),
"reading_direction": (directions,),
"border_thickness": ("INT", {"default": 5, "min": 0, "max": 1024}),
"outline_thickness": ("INT", {"default": 2, "min": 0, "max": 1024}),
"outline_color": (COLORS,),
"panel_color": (COLORS,),
"background_color": (COLORS,),
},
"optional": {
"images": ("IMAGE",),
"custom_panel_layout": ("STRING", {"multiline": False, "default": "H123"}),
"outline_color_hex": ("STRING", {"multiline": False, "default": "#000000"}),
"panel_color_hex": ("STRING", {"multiline": False, "default": "#000000"}),
"bg_color_hex": ("STRING", {"multiline": False, "default": "#000000"}),
}
}
RETURN_TYPES = ("IMAGE", "MASK", "STRING")
RETURN_NAMES = ("image", "masks", "show_help")
FUNCTION = "layout"
CATEGORY = icons.get("Comfyroll/Graphics/Template")
def layout(self, page_width, page_height, template, reading_direction,
border_thickness, outline_thickness,
outline_color, panel_color, background_color,
images=None, custom_panel_layout='G44',
outline_color_hex='#000000', panel_color_hex='#000000', bg_color_hex='#000000'):
panelmasks = []
k = 0
len_images = 0
# Convert tensor images to PIL
if images is not None:
images = [tensor2pil(image) for image in images]
len_images = len(images)
# Convert colors
outline_color = get_color_values(outline_color, outline_color_hex, color_mapping)
panel_color = get_color_values(panel_color, panel_color_hex, color_mapping)
bg_color = get_color_values(background_color, bg_color_hex, color_mapping)
# Create page with bg color
size = (page_width - (2 * border_thickness), page_height - (2 * border_thickness))
page = Image.new('RGB', size, bg_color)
if template == "custom":
template = custom_panel_layout
def make_mask(i, j, w, h, full_size, border_thickness=0, outline_thickness=0):
# Create black mask
mask_img = Image.new("L", full_size, 0)
mask_draw = ImageDraw.Draw(mask_img)
# Match the way panels are drawn in create_and_paste_panel
x0 = x + outline_thickness + border_thickness
y0 = y + outline_thickness + border_thickness
x1 = x + w + outline_thickness + border_thickness
y1 = y + h + outline_thickness + border_thickness
mask_draw.rectangle([x0, y0, x1, y1], fill=255)
# Expand for final page border (outermost margin)
if border_thickness > 0:
mask_img = ImageOps.expand(mask_img, border_thickness, fill=0)
# Convert to RGB tensor
out = np.array(mask_img.convert("RGB")).astype(np.float32) / 255.0
return torch.from_numpy(out).unsqueeze(0)
def make_mask(i, j, w, h, full_size, border_thickness=0, outline_thickness=0):
# Create black mask
mask_img = Image.new("L", full_size, 0)
mask_draw = ImageDraw.Draw(mask_img)
# Compute panel coordinates based on i/j grid position
x0 = j * (w + 2 * outline_thickness + 2 * border_thickness) + border_thickness + outline_thickness
y0 = i * (h + 2 * outline_thickness + 2 * border_thickness) + border_thickness + outline_thickness
x1 = x0 + w
y1 = y0 + h
# Draw white panel region
mask_draw.rectangle([x0, y0, x1, y1], fill=255)
# Expand for final page border (outermost margin)
if border_thickness > 0:
mask_img = ImageOps.expand(mask_img, border_thickness, fill=0)
# Convert to float32 torch mask in range 0.0–1.0
out = np.array(mask_img).astype(np.float32) / 255.0 # [H,W]
out = torch.from_numpy(out).unsqueeze(0) # [1,H,W]
return out
# Panel placement
first_char = template[0]
if first_char == "G":
rows = int(template[1])
columns = int(template[2])
panel_width = (page.width - (2 * columns * (border_thickness + outline_thickness))) // columns
panel_height = (page.height - (2 * rows * (border_thickness + outline_thickness))) // rows
# Row loop
for i in range(rows):
# Column Loop
for j in range(columns):
# Draw the panel
create_and_paste_panel(page, border_thickness, outline_thickness,
panel_width, panel_height, page.width,
panel_color, bg_color, outline_color,
images, i, j, k, len_images, reading_direction)
panelmasks.append(
make_mask(i, j, panel_width, panel_height, page.size,
border_thickness, outline_thickness)
)
k += 1
elif first_char == "H":
rows = len(template) - 1
panel_height = (page.height - (2 * rows * (border_thickness + outline_thickness))) // rows
for i in range(rows):
columns = int(template[i+1])
panel_width = (page.width - (2 * columns * (border_thickness + outline_thickness))) // columns
for j in range(columns):
# Draw the panel
create_and_paste_panel(page, border_thickness, outline_thickness,
panel_width, panel_height, page.width,
panel_color, bg_color, outline_color,
images, i, j, k, len_images, reading_direction)
panelmasks.append(
make_mask(i, j, panel_width, panel_height, page.size,
border_thickness, outline_thickness)
)
k += 1
elif first_char == "V":
columns = len(template) - 1
panel_width = (page.width - (2 * columns * (border_thickness + outline_thickness))) // columns
for j in range(columns):
rows = int(template[j+1])
panel_height = (page.height - (2 * rows * (border_thickness + outline_thickness))) // rows
for i in range(rows):
# Draw the panel
create_and_paste_panel(page, border_thickness, outline_thickness,
panel_width, panel_height, page.width,
panel_color, bg_color, outline_color,
images, i, j, k, len_images, reading_direction)
panelmasks.append(
make_mask(i, j, panel_width, panel_height, page.size,
border_thickness, outline_thickness)
)
k += 1
# Add a border to the page
if border_thickness > 0:
page = ImageOps.expand(page, border_thickness, bg_color)
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Template-Nodes#cr-comic-panel-templates"
# Convert final page and masks to tensors
comic_tensor = pil2tensor(page)
if panelmasks:
masks_tensor = torch.cat(panelmasks, 0) # [N,H,W]
else:
masks_tensor = torch.zeros((1, size[1], size[0]), dtype=torch.float32) # empty mask
return (comic_tensor, masks_tensor, show_help)`
It still outputs the standard panel of an image or default template.
It also outputs and mask batch.
If this node could do something like that and better crop the images to the panels that would of course be cool. Anyway I will try to work on it.
I also think it would be useful to output a few other things like a mask of outline, a mask of boarder, or maybe combined whitespace mask.
But I am open to suggestions. This is not as polished as I plan to get it but life has a way of getting away and I wanted to post what I have, while I think to do it.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
I just recently became aware of this CR_ComicPanelTemplates node. It seems useful. And the more practical way to make use of something like this is probably either use the output as is or open it in photo editing.
But I am a little fascinated by those generations of comics using models. They are a bit FOCUSED in what they are trained on but the ones that are more general aesthetic models seem cool but not really practical for making something worth while.
Here is what I found in the SDXL family of loras.
Vixon's Anime/Manga Styles - manga panels
Vixon's Pony Styles - Soft Panels
Comic page generator | Illustrious Manga Boxes
XL Manga | Comic page generator
Comic Book Page style (Anime + Manga + Western Comics) XL
Manga / Comic Page Templates
But I think the key to that might be to give them a lot of guidance in the form of conditioning from prompt
conditioning masks
ConditioningSetMaskAndCombine5 https://github.com/kijai/ComfyUI-KJNodes
as well as controlnet, InstantID, Reactor, and ipAdapters.
as well as using the mask to inpaint generations into the panel.
Anyway this is a long way to say having it output masks for the panels would help in doing this sort of thing.
I tried to do this with subgraphs but it became a pile of spaghetti. Worse it didn't work.
So I did it the proper way.
`class CR_ComicPanelTemplatesMasks:
It still outputs the standard panel of an image or default template.

It also outputs and mask batch.
I made a version that uses a subgraph to instead have 20 static image inputs and 20 static mask outputs.
I plan on working on this more.
There is a node I use in this workflow called "creat_mask_batch_input"
https://github.com/cardenluo/ComfyUI-Apt_Preset/blob/e679d7eea90c8fa97b45a14db5b729b5862a6a59/web/mul_input_count.js#L1
It uses a js function to update inputs to match a count number set by the user. default 3, max 50.
If this node could do something like that and better crop the images to the panels that would of course be cool. Anyway I will try to work on it.
I also think it would be useful to output a few other things like a mask of outline, a mask of boarder, or maybe combined whitespace mask.
But I am open to suggestions. This is not as polished as I plan to get it but life has a way of getting away and I wanted to post what I have, while I think to do it.
Beta Was this translation helpful? Give feedback.
All reactions