Skip to content

Commit 5375016

Browse files
authored
Update AILab_ImageMaskTools.py
1 parent b74e3eb commit 5375016

File tree

1 file changed

+165
-72
lines changed

1 file changed

+165
-72
lines changed

AILab_ImageMaskTools.py

Lines changed: 165 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,10 @@ def execute(self, mask_opacity, mask_color, filename_prefix="ComfyUI", image=Non
294294
mask_image[:, :, :, 1] = g
295295
mask_image[:, :, :, 2] = b
296296

297-
preview, = ImageCompositeMasked.composite(self, image, mask_image, 0, 0, True, mask_adjusted)
297+
if hasattr(ImageCompositeMasked, "execute"):
298+
preview, = ImageCompositeMasked.execute(image, mask_image, 0, 0, True, mask_adjusted)
299+
else:
300+
preview, = ImageCompositeMasked.composite(image, mask_image, 0, 0, True, mask_adjusted)
298301

299302
if preview is None:
300303
preview = empty_image(64, 64)
@@ -1794,105 +1797,194 @@ class AILab_ImageCompare:
17941797
def __init__(self):
17951798
self.font_size = 20
17961799
self.padding = 10
1797-
self.bg_color = "white"
1798-
self.font_color = "black"
1799-
self.text_align = "center"
1800+
#self.text_align = "center"
18001801

18011802
@classmethod
18021803
def INPUT_TYPES(cls):
18031804
return {
18041805
"required": {
1806+
"text1": ("STRING", {"default": "Image 1"}),
1807+
"text2": ("STRING", {"default": "Image 2"}),
1808+
"text3": ("STRING", {"default": "Image 3"}),
1809+
"size_base": (["largest", "smallest", "image1", "image2", "image3"], {"default": "largest"}),
1810+
"text_color": ("COLORCODE", {"default": "#000000"}),
1811+
"bg_color": ("COLORCODE", {"default": "#FFFFFF"}),
1812+
},
1813+
"optional": {
18051814
"image1": ("IMAGE",),
18061815
"image2": ("IMAGE",),
1807-
"text1": ("STRING", {"default": "image 1"}),
1808-
"text2": ("STRING", {"default": "image 2"}),
1809-
}
1816+
"image3": ("IMAGE",),
1817+
},
18101818
}
18111819

18121820
RETURN_TYPES = ("IMAGE",)
18131821
FUNCTION = "generate"
18141822
CATEGORY = "🧪AILab/🖼️IMAGE"
18151823

1816-
def get_font(self) -> ImageFont.FreeTypeFont:
1824+
def get_font(self):
18171825
try:
1818-
if os.name == 'nt':
1826+
if os.name == "nt":
18191827
return ImageFont.truetype("arial.ttf", self.font_size)
1828+
elif os.path.exists("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"):
1829+
return ImageFont.truetype(
1830+
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
1831+
self.font_size,
1832+
)
18201833
else:
1821-
return ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", self.font_size)
1822-
except:
1823-
base_font = ImageFont.load_default()
1824-
scale_factor = self.font_size / 10
1825-
return ImageFont.TransposedFont(base_font, scale=scale_factor)
1834+
return ImageFont.load_default()
1835+
except Exception:
1836+
return ImageFont.load_default()
18261837

1827-
def create_text_panel(self, width: int, text: str) -> Image.Image:
1838+
def create_text_panel(self, width, text):
18281839
font = self.get_font()
1829-
1830-
temp_img = Image.new('RGB', (width, self.font_size * 4), self.bg_color)
1840+
temp_img = Image.new("RGB", (width, self.font_size * 2), self.bg_color)
18311841
temp_draw = ImageDraw.Draw(temp_img)
1832-
1833-
text_bbox = temp_draw.textbbox((0, self.font_size), text, font=font)
1842+
text_bbox = temp_draw.textbbox((0, 0), text, font=font)
18341843
text_width = text_bbox[2] - text_bbox[0]
18351844
text_height = text_bbox[3] - text_bbox[1]
1836-
1837-
final_height = int(text_height * 1.5)
1838-
panel = Image.new('RGB', (width, final_height), self.bg_color)
1845+
final_height = max(int(text_height * 1.5), self.font_size * 2)
1846+
panel = Image.new("RGB", (width, final_height), self.bg_color)
18391847
draw = ImageDraw.Draw(panel)
1840-
18411848
x = (width - text_width) // 2
18421849
y = (final_height - text_height) // 2
1843-
1844-
draw.text((x, y), text, font=font, fill=self.font_color)
1850+
draw.text((x, y), text, font=font, fill=self.text_color)
18451851
return panel
18461852

1847-
def process_image(self, img: Image.Image, target_size: tuple) -> Image.Image:
1848-
target_width, target_height = target_size
1849-
img_width, img_height = img.size
1850-
1851-
scale_width = target_width / img_width
1852-
scale_height = target_height / img_height
1853-
scale = max(scale_width, scale_height)
1854-
1855-
new_width = int(img_width * scale)
1856-
new_height = int(img_height * scale)
1857-
1858-
resized = resize_image(img, new_width, new_height)
1859-
left = (new_width - target_width) // 2
1860-
top = (new_height - target_height) // 2
1861-
right = left + target_width
1862-
bottom = top + target_height
1863-
1864-
return resized.crop((left, top, right, bottom))
1853+
def _select_base_image(self, pil_map, size_base):
1854+
if size_base in ("image1", "image2", "image3") and size_base in pil_map:
1855+
return size_base
1856+
if size_base == "smallest":
1857+
best_label = None
1858+
best_area = float('inf')
1859+
for label, img in pil_map.items():
1860+
area = img.width * img.height
1861+
if area < best_area:
1862+
best_area = area
1863+
best_label = label
1864+
return best_label
1865+
if size_base != "largest":
1866+
print(
1867+
f"Warning: size_base '{size_base}' is not available, fallback to 'largest'."
1868+
)
1869+
best_label = None
1870+
best_area = -1
1871+
for label, img in pil_map.items():
1872+
area = img.width * img.height
1873+
if area > best_area:
1874+
best_area = area
1875+
best_label = label
1876+
return best_label
1877+
1878+
def generate(self, text1, text2, text3, size_base="largest", text_color="#000000", bg_color="#FFFFFF", image1=None, image2=None, image3=None,):
1879+
self.bg_color = bg_color
1880+
self.text_color = text_color
1881+
1882+
tensors = []
1883+
texts = []
1884+
labels = []
1885+
1886+
if image1 is not None and hasattr(image1, "shape") and image1.shape[0] > 0:
1887+
tensors.append(image1)
1888+
texts.append(text1)
1889+
labels.append("image1")
1890+
if image2 is not None and hasattr(image2, "shape") and image2.shape[0] > 0:
1891+
tensors.append(image2)
1892+
texts.append(text2)
1893+
labels.append("image2")
1894+
if image3 is not None and hasattr(image3, "shape") and image3.shape[0] > 0:
1895+
tensors.append(image3)
1896+
texts.append(text3)
1897+
labels.append("image3")
1898+
1899+
if len(tensors) < 2:
1900+
print("Warning: At least two images are required.")
1901+
return (torch.zeros((1, 64, 64, 3)),)
18651902

1866-
def generate(self, image1, image2, text1, text2):
1867-
img1 = tensor2pil(image1)
1868-
img2 = tensor2pil(image2)
1869-
1870-
if img2.size != img1.size:
1871-
img2 = resize_image(img2, img1.size[0], img1.size[1])
1872-
1873-
panel1 = None if not text1.strip() else self.create_text_panel(img1.width, text1)
1874-
panel2 = None if not text2.strip() else self.create_text_panel(img2.width, text2)
1875-
1876-
total_width = img1.width + img2.width + self.padding * 3
1877-
img_height = img1.height
1878-
panel_height = (panel1.height if panel1 else 0) if (panel1 or panel2) else 0
1879-
total_height = img_height + (panel_height + self.padding if panel_height > 0 else 0) + self.padding * 2
1880-
1881-
result = Image.new('RGB', (total_width, total_height), self.bg_color)
1903+
batch_sizes = [t.shape[0] for t in tensors]
1904+
batch_size = min(batch_sizes)
1905+
if len(set(batch_sizes)) > 1:
1906+
print(
1907+
f"Warning: Input batches have different sizes {batch_sizes}. "
1908+
f"Only processing the minimum size of {batch_size}."
1909+
)
18821910

1883-
x1 = self.padding
1884-
x2 = x1 + img1.width + self.padding
1885-
y = self.padding
1886-
1887-
result.paste(img1, (x1, y))
1888-
result.paste(img2, (x2, y))
1889-
1890-
if panel1:
1891-
result.paste(panel1, (x1, y + img_height + self.padding))
1892-
if panel2:
1893-
result.paste(panel2, (x2, y + img_height + self.padding))
1894-
1895-
return (pil2tensor(result),)
1911+
output_images = []
1912+
1913+
for i in range(batch_size):
1914+
pil_map = {}
1915+
for t, label in zip(tensors, labels):
1916+
frame = t[i].unsqueeze(0)
1917+
img_pil = tensor2pil(frame)
1918+
pil_map[label] = img_pil
1919+
1920+
base_label = self._select_base_image(pil_map, size_base)
1921+
base_img = pil_map[base_label]
1922+
base_h = base_img.height
1923+
1924+
resized_pils = []
1925+
for label in labels:
1926+
img = pil_map[label]
1927+
w, h = img.size
1928+
1929+
if w == 0 or h == 0:
1930+
resized_pils.append(Image.new("RGB", (1, 1), self.bg_color))
1931+
continue
1932+
1933+
if label == base_label:
1934+
resized_pils.append(img)
1935+
continue
1936+
1937+
scale = base_h / h
1938+
new_h = base_h
1939+
new_w = max(1, int(round(w * scale)))
1940+
1941+
if img.size != (new_w, new_h):
1942+
img = resize_image(img, new_w, new_h)
1943+
resized_pils.append(img)
1944+
1945+
panels = []
1946+
for img, text in zip(resized_pils, texts):
1947+
if text.strip():
1948+
panels.append(self.create_text_panel(img.width, text))
1949+
else:
1950+
panels.append(None)
1951+
1952+
total_width = (
1953+
sum(img.width for img in resized_pils)
1954+
+ self.padding * (len(resized_pils) + 1)
1955+
)
1956+
img_height = max(img.height for img in resized_pils)
1957+
1958+
panel_heights = [p.height for p in panels if p is not None]
1959+
panel_height = max(panel_heights) if panel_heights else 0
1960+
panel_area_height = (panel_height + self.padding) if panel_height > 0 else 0
1961+
1962+
total_height = img_height + panel_area_height + self.padding * 2
1963+
1964+
result_pil = Image.new("RGB", (total_width, total_height), self.bg_color)
1965+
1966+
y_img = self.padding
1967+
y_panel = y_img + img_height + self.padding
1968+
1969+
x = self.padding
1970+
for img, panel in zip(resized_pils, panels):
1971+
result_pil.paste(img, (x, y_img))
1972+
if panel is not None and panel_height > 0:
1973+
y_offset = (
1974+
y_panel + (panel_height - panel.height)
1975+
if panel.height < panel_height
1976+
else y_panel
1977+
)
1978+
result_pil.paste(panel, (x, y_offset))
1979+
x += img.width + self.padding
1980+
1981+
output_images.append(pil2tensor(result_pil))
1982+
1983+
if not output_images:
1984+
return (torch.zeros((1, 64, 64, 3)),)
1985+
1986+
final_batch_tensor = torch.cat(output_images, dim=0)
1987+
return (final_batch_tensor,)
18961988

18971989
# Color Input node
18981990
class AILab_ColorInput:
@@ -2177,3 +2269,4 @@ def resize(self, image, width, height, scale_by, upscale_method, resize_mode, pa
21772269
"AILab_ColorInput": "Color Input (RMBG) 🎨",
21782270
"AILab_ImageMaskResize": "Image Mask Resize (RMBG) 🖼️🎭"
21792271
}
2272+

0 commit comments

Comments
 (0)