diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..08abfced7858490469794452053528191d8d92df --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +assets/examples/10_style.png diff --git a/app1.py b/app1.py new file mode 100644 index 0000000000000000000000000000000000000000..b4faece90ab5e61b1c1f985917dbfb4ae0bdd0a9 --- /dev/null +++ b/app1.py @@ -0,0 +1,393 @@ +##!/usr/bin/python3 +# -*- coding: utf-8 -*- +# @Time : 2024-01-29 +# @Author : Junjie He +import json +import os +import time +import uuid + +import cv2 +import gradio as gr +import numpy as np +from PIL import Image +from modelscope.outputs import OutputKeys +from modelscope.pipelines import pipeline +from modelscope.utils.constant import Tasks + +from src.generation import call_generation +from src.util import upload_np_2_oss, upload_json_string_2_oss, upload_preprocess, merge_images + +universal_matting = pipeline(Tasks.universal_matting, model='damo/cv_unet_universal-matting') + +img = "assets/image_gallery_en/" +files = os.listdir(img) +files = [file for file in files if file.lower().endswith(('.png', '.jpg', '.jpeg'))] +files = sorted(files) +basic_usage = [] +showcases = [] +for idx, name in enumerate(files): + temp = os.path.join(os.path.dirname(__file__), img, name) + if idx < 4: + basic_usage.append(temp) + else: + showcases.append(temp) + +# - - - - - examples - - - - - # +ep = "assets/examples" +# Layout, Style, Color, Subject, Prompt,Strict Layout Edge,Layout Content Scale, Automatic Image Matting +image_examples = [ + [0, f"{ep}/00_layout.png", f"{ep}/empty.png", f"{ep}/empty.png", f"{ep}/empty.png", "", True, 0., True], + [1, f"{ep}/01_layout.png", f"{ep}/empty.png", f"{ep}/empty.png", f"{ep}/empty.png", "", True, 0.8, True], + [2, f"{ep}/empty.png", f"{ep}/02_style.png", f"{ep}/empty.png", f"{ep}/empty.png", "", True, 0.8, True], + [3, f"{ep}/empty.png", f"{ep}/03_style.png", f"{ep}/empty.png", f"{ep}/empty.png", "", True, 0.8, True], + [4, f"{ep}/empty.png", f"{ep}/empty.png", f"{ep}/04_color.png", f"{ep}/empty.png", "A moose, Merry Christmas", True, 0.8, True], + [5, f"{ep}/empty.png", f"{ep}/empty.png", f"{ep}/05_color.png", f"{ep}/empty.png", "A photo about cherry blossom", + True, 0.8, True], + [6, f"{ep}/06_layout.png", f"{ep}/06_style.jpeg", f"{ep}/empty.png", f"{ep}/empty.png", "", True, 0.8, True], + [7, f"{ep}/07_layout.jpeg", f"{ep}/07_style.jpeg", f"{ep}/empty.png", f"{ep}/empty.png", "", True, 0.8, True], + [8, f"{ep}/empty.png", f"{ep}/08_style.png", f"{ep}/08_color.jpeg", f"{ep}/empty.png", "", True, 0.8, True], + [9, f"{ep}/empty.png", f"{ep}/09_style.png", f"{ep}/empty.png", f"{ep}/base_image1.jpeg", "", True, 0.8, True], + [10, f"{ep}/10_layout.png", f"{ep}/10_style.png", f"{ep}/empty.png", f"{ep}/base_image2.png", "", True, 0.8, False], + [11, f"{ep}/11_layout.png", f"{ep}/empty.png", f"{ep}/empty.png", f"{ep}/base_image4.png", "", False, 0.8, False], + [12, f"{ep}/12_layout.png", f"{ep}/empty.png", f"{ep}/empty.png", f"{ep}/base_image3.png", "", False, 0.8, False], +] + +example_images = [ + [0, f"{ep}/layout_image1.jpeg", None, None, None], + [1, f"{ep}/layout_image1.jpeg", None, None, None], + [2, None, f"{ep}/style_image1.jpeg", None, None], + [3, None, f"{ep}/style_image1.jpeg", None, None], + [4, None, None, f"{ep}/color_image1.jpeg", None], + [5, None, None, f"{ep}/color_image3.jpeg", None], + [6, f"{ep}/layout_image2.jpeg", f"{ep}/style_image2.jpeg", None, None], + [7, f"{ep}/layout_image3.jpeg", f"{ep}/style_image3.jpeg", None, None], + [8, None, f"{ep}/style_image4.jpeg", f"{ep}/color_image2.jpeg", None], + [9, None, f"{ep}/style_image6.jpeg", None, f"{ep}/09_base.png"], + [10, f"{ep}/layout_image5.jpeg", f"{ep}/style_image5.jpeg", None, f"{ep}/10_base.png"], + [11, f"{ep}/layout_image7.jpeg", None, None, f"{ep}/11_base.png"], + [12, f"{ep}/layout_image6.jpeg", None, None, f"{ep}/12_base.png"], +] + +example_masks = [ + [0, None, None, None], + [1, f"{ep}/layout_image1_mask.png", None, None], + [2, None, f"{ep}/style_image1_mask.png", None], + [3, None, f"{ep}/style_image1_mask2.png", None], + [4, None, None, None], + [5, None, None, f"{ep}/color_image3_mask.png"], + [6, None, None, None], + [7, None, None, None], + [8, None, None, None], + [9, None, f"{ep}/style_image6_mask.png", None], + [10, f"{ep}/layout_image5_mask.png", None, None], + [11, None, None, None], + [12, f"{ep}/layout_image6_mask.png", None, None], +] + + +def process_example(example_idx, pil_layout_image, pil_style_image, pil_color_image, pil_base_image_rgba, + prompt, strict_edge, layout_scale, preprocess_base_image): + _, layout_image, style_image, color_image, base_image_rgba = example_images[example_idx] + _, layout_mask, style_mask, color_mask = example_masks[example_idx] + + pil_layout_image = None if layout_image is None else pil_layout_image + pil_style_image = None if style_image is None else pil_style_image + pil_color_image = None if color_image is None else pil_color_image + pil_base_image_rgba = None if base_image_rgba is None else pil_base_image_rgba + + return pil_layout_image, layout_mask, pil_style_image, style_mask, pil_color_image, color_mask, \ + pil_base_image_rgba, prompt, strict_edge, layout_scale, preprocess_base_image + + +def process(pil_base_image_rgba=None, preprocess_base_image=False, + pil_layout_image_dict=None, layout_scale=1.0, edge_consistency=0.5, + strict_edge=False, + pil_color_image_dict=None, color_scale=1.0, + pil_style_image_dict=None, style_scale=1.0, prompt="best quality", negative_prompt="", + pil_layout_mask=None, pil_style_mask=None, pil_color_mask=None): + request_id = time.strftime('%Y%m%d-', time.localtime(time.time())) + str(uuid.uuid4()) + + output_aspect_ratio = 1. + matting_flag = False + + if pil_base_image_rgba is None: + base_image_url = "" + pil_fg_mask = None + else: + if preprocess_base_image: + matting_flag = True + orig_image = np.array(pil_base_image_rgba) + orig_alpha = np.array(pil_base_image_rgba)[..., -1] + matting_alpha = universal_matting(pil_base_image_rgba)[OutputKeys.OUTPUT_IMG][..., -1] + orig_image[..., -1] = ((matting_alpha > 200) * (orig_alpha > 200) * 255.).astype(np.uint8) + pil_base_image_rgba = Image.fromarray(orig_image) + pil_fg_mask = pil_base_image_rgba.split()[-1] + + w, h = pil_base_image_rgba.size + output_aspect_ratio = max(1.0 * w / h, 1.0 * h / w) + if output_aspect_ratio > 2: + raise gr.Error("Input of subject images with aspect ratio exceeding 2 is not supported") + if min(w, h) > 1536: + raise gr.Error("Input of subject images with the shorter side exceeding 1536 pixels is not supported") + base_image_url = upload_np_2_oss(np.array(pil_base_image_rgba), request_id + "_base.png") + + if pil_layout_image_dict is None: + layout_image_url = "" + else: + np_layout_image = np.array(pil_layout_image_dict["image"].convert("RGBA")) + np_layout_image, np_layout_alpha = np_layout_image[..., :3], np_layout_image[..., 3] + np_layout_mask = np.array(pil_layout_image_dict["mask"].convert("L")) + if pil_layout_mask is None: + np_layout_mask = ((np_layout_alpha > 127) * (np_layout_mask < 127) * 255.).astype(np.uint8) + else: + np_layout_mask = ((np_layout_alpha > 127) * (np_layout_mask < 127) * + (np.array(pil_layout_mask) > 127) * 255.).astype(np.uint8) + layout_image_url = upload_np_2_oss( + np.concatenate([np_layout_image, np_layout_mask[..., None]], axis=-1), request_id + "_layout.png" + ) + if pil_base_image_rgba is None: + h, w, c = np_layout_image.shape + output_aspect_ratio = max(1.0 * w / h, 1.0 * h / w) + if output_aspect_ratio > 2: + raise gr.Error("Input of layout images with aspect ratio exceeding 2 is not supported") + if min(w, h) > 1536: + raise gr.Error("Input of layout images with the shorter side exceeding 1536 pixels is not supported") + + if pil_style_image_dict is None: + style_image_url = "" + else: + np_style_image = np.array(pil_style_image_dict["image"].convert("RGBA")) + np_style_image, np_style_alpha = np_style_image[..., :3], np_style_image[..., 3] + np_style_mask = np.array(pil_style_image_dict["mask"].convert("L")) + if pil_style_mask is None: + np_style_mask = ((np_style_alpha > 127) * (np_style_mask < 127) * 255.).astype(np.uint8) + else: + np_style_mask = ((np_style_alpha > 127) * (np_style_mask < 127) * + (np.array(pil_style_mask) > 127) * 255.).astype(np.uint8) + style_image_url = upload_np_2_oss( + np.concatenate([np_style_image, np_style_mask[..., None]], axis=-1), request_id + "_style.png" + ) + + if pil_color_image_dict is None: + color_image_url = "" + else: + np_color_image = np.array(pil_color_image_dict["image"].convert("RGBA")) + np_color_image, np_color_alpha = np_color_image[..., :3], np_color_image[..., 3] + np_color_mask = np.array(pil_color_image_dict["mask"].convert("L")) + if pil_color_mask is None: + np_color_mask = ((np_color_alpha > 127) * (np_color_mask < 127) * 255.).astype(np.uint8) + else: + np_color_mask = ((np_color_alpha > 127) * (np_color_mask < 127) * + (np.array(pil_color_mask) > 127) * 255.).astype(np.uint8) + color_image_url = upload_np_2_oss( + np.concatenate([np_color_image, np_color_mask[..., None]], axis=-1), request_id + "_color.png" + ) + + res = call_generation(base_image_url=base_image_url, layout_image_url=layout_image_url, + color_image_url=color_image_url, style_image_url=style_image_url, + strict_edge=int(strict_edge), layout_scale=int(layout_scale * 10), + edge_consistency=int(edge_consistency * 10), color_scale=int(color_scale * 10), + style_scale=int(style_scale * 10), prompt=prompt, negative_prompt=negative_prompt, + output_aspect_ratio=output_aspect_ratio) + + for idx, r in enumerate(res): + upload_np_2_oss(np.array(r), request_id + f"_{idx}.jpg") + + if matting_flag: + res.append(pil_base_image_rgba) + return res, request_id, True, pil_fg_mask + + +if __name__ == "__main__": + + block = gr.Blocks( + title="TransferAnything", + css="assets/css/style.css", + theme=gr.themes.Soft( + radius_size=gr.themes.sizes.radius_none, + text_size=gr.themes.sizes.text_md + )).queue(concurrency_count=3) + with block: + with gr.Row(): + with gr.Column(): + gr.HTML(f""" +
+

ImageSynthesizer: Enables a Versatile Visual Information Transfer for Creative Image Synthesis

+
+

ImageSynthesizer supports transferring various visual information from any area of any image to create new compositions, offering higher freedom and flexibility in image synthesis. Currently, it supports the transfer of layout, color, style, and pixel content, with more visual information transfer capabilities.

+
+
+ """) + + #with gr.Tabs(elem_classes=["Tab"]): + # with gr.TabItem("Image Gallery"): + # gr.Gallery(label="Basic Usage", value=basic_usage, height=400, columns=4, object_fit="scale-down") + # gr.Gallery(label="Advanced Combinations", value=showcases, height=1200, columns=4, object_fit="scale-down") + # with gr.TabItem("Image Creation"): + with gr.Row(): + with gr.Column(scale=1): + ... + with gr.Column(scale=3): + gr.Image(value="assets/banner/banner.png", width=1024, show_label=False, + show_download_button=False) + with gr.Column(scale=1): + ... + with gr.Accordion(label="🧭 Instructions:", open=True, elem_id="accordion"): + with gr.Row(equal_height=True): + # with gr.Row(elem_id="ShowCase"): + # gr.Image(value="assets/banner/ra.gif") + gr.Markdown(""" + - ⭐️ step1: (Optional) Upload or select a set of images from the examples for the "Layout", "Style", and "Color" reference. Mix and match freely, no need to select all. + - ⭐️ step2: (Optional) Use brush to erase areas in the layout, style, and color reference images (if present) that you do not want transferred. + - ⭐️ step3: (Optional) Upload an RGBA image to the "Subject" tab, with the alpha channel indicating the subject you wish to preserve at the pixel level (or upload a regular RGB image and check the automatic image matting option, which will automatically segment the subject for you; the default is the latter). + - ⭐️ step4: Click "Run" to start the generation process. + - ⭐️ step5: (Optional) Additionally, prompt input is supported, as well as control over advanced parameters such as layout edge consistency and conditional weights. Feel free to try these features. + """) + # with gr.Row(equal_height=True): + + with gr.Row(): + with gr.Column(scale=1, min_width=160): + with gr.Tabs(elem_classes=["feedback"]): + with gr.TabItem("Layout (Optional)"): + pil_layout_image_dict = gr.ImageMask(source='upload', type="pil", show_label=False, + image_mode="RGBA") + pil_layout_image = gr.Image(interactive=False, type="pil", visible=False, label="Layout") + pil_layout_mask = gr.Image(interactive=False, type="pil", visible=False, label="Layout Mask", + image_mode="L") + with gr.Box(): + with gr.Accordion(label="Layout Parameters", open=False, elem_id="accordion"): + with gr.TabItem("Layout Edge"): + strict_edge = gr.Checkbox(label="Strict", value=True) + strict_edge_mirror = gr.Checkbox(label="Strict Layout Edge", visible=False) + edge_consistency = gr.Slider(label="Degree of Consistency (If Not Strict)", minimum=0.0, + maximum=1.0, + step=0.1, value=0.8, interactive=True) + with gr.TabItem("Layout Content"): + layout_scale = gr.Slider(label="Scale", minimum=0.0, maximum=1.0, step=0.1, + value=0.8, + interactive=True) + layout_scale_mirror = gr.Slider(label="Layout Content Scale", visible=False) + + with gr.Column(scale=1, min_width=160): + with gr.Tabs(elem_classes=["feedback"]): + with gr.TabItem("Style (Optional)"): + pil_style_image_dict = gr.ImageMask(source='upload', type="pil", show_label=False, + image_mode="RGBA") + pil_style_image = gr.Image(interactive=False, type="pil", visible=False, label="Style") + pil_style_mask = gr.Image(interactive=False, type="pil", visible=False, label="Style Mask", + image_mode="L") + with gr.Box(): + with gr.Accordion(label="Style Parameters", open=False, elem_id="accordion"): + style_scale = gr.Slider(label="Scale", minimum=0.0, maximum=1.0, step=0.1, + value=0.8, interactive=True) + with gr.Column(scale=1, min_width=160): + with gr.Tabs(elem_classes=["feedback"]): + with gr.TabItem("Color (Optional, recommended for use with prompt)"): + pil_color_image_dict = gr.ImageMask(source='upload', type="pil", show_label=False, + image_mode="RGBA") + pil_color_image = gr.Image(interactive=False, type="pil", visible=False, label="Color") + pil_color_mask = gr.Image(interactive=False, type="pil", visible=False, label="Color Mask", + image_mode="L") + with gr.Box(): + with gr.Accordion(label="Color Parameters", open=False, elem_id="accordion"): + color_scale = gr.Slider(label="Scale", minimum=0.0, maximum=1.0, step=0.1, + value=0.8, interactive=True) + with gr.Row(): + with gr.Column(scale=1, min_width=160): + with gr.Tabs(elem_classes=["feedback"]): + with gr.TabItem("Subject (Optional)"): + pil_base_image_rgba = gr.Image(source='upload', + interactive=True, show_label=False, + type="pil", image_mode="RGBA", tool="editor") + pil_base_image_rgba_mirror = gr.Image(label="Subject", image_mode="RGBA", visible=False) + with gr.Box(): + preprocess_base_image = gr.Checkbox(label="Automatic Image Matting", value=True) + pil_fg_mask = gr.Image(interactive=False, type="pil", image_mode="L", visible=False) + run_button = gr.Button("Run", elem_id="btn") + with gr.Accordion("", open=True, elem_id="accordion1"): + prompt = gr.Textbox(value="", label='Prompt', lines=1, interactive=True) + prompt_mirror = gr.Textbox(label='Prompt', visible=False) + negative_prompt = gr.Textbox(value="", label='Negative prompt', lines=1, + interactive=True) + with gr.Column(scale=2, min_width=160): + with gr.Tabs(elem_classes=["feedback"]): + with gr.TabItem("Outputs"): + result_gallery = gr.Gallery(label='Output', show_label=False, elem_id="gallery", + preview=True) + recommend = gr.Button("Recommend results to Image Gallery", elem_id="recBut") + request_id = gr.State(value="") + gallery_flag = gr.State(value=False) + with gr.Row(): + with gr.Box(): + example_idx = gr.Slider(label="Index", visible=False, value=0) + example = gr.Examples( + label="Input Examples", + examples=image_examples, + inputs=[example_idx, + pil_layout_image, pil_style_image, pil_color_image, pil_base_image_rgba_mirror, + prompt_mirror, strict_edge_mirror, layout_scale_mirror, preprocess_base_image], + outputs=[pil_layout_image_dict, pil_layout_mask, pil_style_image_dict, pil_style_mask, + pil_color_image_dict, pil_color_mask, pil_base_image_rgba, + prompt, strict_edge, layout_scale, preprocess_base_image], + fn=process_example, + run_on_click=True, + examples_per_page=20 + ) + with gr.Column(): + gr.HTML(f""" +
+
+ Project Page +
+
+ """) + + + def upload_to_img_gallery(pil_base_image_rgba, pil_layout_image_dict, pil_style_image_dict, + pil_color_image_dict, pil_fg_mask, prompt, negative_prompt, res, re_id, flag, + strict_edge, edge_consistency, layout_scale, style_scale, color_scale, + preprocess_base_image): + if flag: + np_out_base_image, np_out_layout_image, np_out_style_image, np_out_color_image = upload_preprocess( + pil_base_image_rgba, pil_layout_image_dict, pil_style_image_dict, pil_color_image_dict, pil_fg_mask) + np_out_images = [np_out_base_image, np_out_layout_image, np_out_style_image, np_out_color_image] + for idx, r in enumerate(res): + if idx < 4: + r = cv2.imread(r['name']) + r = cv2.cvtColor(r, cv2.COLOR_BGR2RGB) + upload_np_2_oss(merge_images(*np_out_images, r, prompt, negative_prompt), + name=re_id + f"_merge_{idx}.jpg", gallery=True) + config = dict( + strict_edge=strict_edge, + edge_consistency=edge_consistency, + layout_scale=layout_scale, + style_scale=style_scale, + color_scale=color_scale, + preprocess_base_image=preprocess_base_image + ) + upload_json_string_2_oss(json.dumps(config), name=re_id + f"_config.txt", gallery=True) + + flag = False + gr.Info("Images have been uploaded and await review.") + else: + gr.Info("No images to recommend, or already suggested once.") + return flag + + + recommend.click( + upload_to_img_gallery, + [pil_base_image_rgba, pil_layout_image_dict, pil_style_image_dict, pil_color_image_dict, pil_fg_mask, + prompt, negative_prompt, result_gallery, request_id, gallery_flag, strict_edge, edge_consistency, + layout_scale, style_scale, color_scale, preprocess_base_image], + [gallery_flag] + ) + + ips = [pil_base_image_rgba, preprocess_base_image, + pil_layout_image_dict, layout_scale, edge_consistency, strict_edge, + pil_color_image_dict, color_scale, + pil_style_image_dict, style_scale, prompt, negative_prompt, + pil_layout_mask, pil_style_mask, pil_color_mask] + run_button.click(fn=process, inputs=ips, outputs=[result_gallery, request_id, gallery_flag, pil_fg_mask]) + + block.launch(share=True) diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100644 index 0000000000000000000000000000000000000000..cbb014dc4d792f94df526411281dc16d31a2985a --- /dev/null +++ b/assets/css/style.css @@ -0,0 +1,71 @@ + +.baselayout { + background: url('https://img.alicdn.com/imgextra/i1/O1CN016hd0V91ilWY5Xr24B_!!6000000004453-2-tps-2882-256.png') no-repeat; +} + +#btn { + background-color: #336699; + color: white; +} + +#recBut { + background-color: #bb5252; + color: white; + width: 30%; + margin: auto; +} + +#btnSEG { + background-color: #D5F3F4; + color: black; +} + +#btnCHAT { + background-color: #B6DBF2; + color: black; +} + +#accordion { + background-color: transparent; +} + +#accordion1 { + background-color: #ecedee; +} + +.feedback button.selected { + background-color: #6699CC; + color: white !important; +} + +.feedback1 button.selected { + background-color: #839ab2; + color: white !important; +} + +.Tab button.selected { + color: red; + font-weight: bold; +} + +#Image { + width: 80%; + margin: auto; +} + +#ShowCase { + width: 30%; + flex: none !important; +} + +#Input { + border-style: solid; + border-width: 1px; + border-color: #000000 +} + +#Seg { + min-width: min(100px, 100%) !important; + width: 100%; + margin: auto; +} diff --git a/assets/examples/00_layout.png b/assets/examples/00_layout.png new file mode 100644 index 0000000000000000000000000000000000000000..9b7eec8e7ef41790417a91bd01f06565ba34ed61 Binary files /dev/null and b/assets/examples/00_layout.png differ diff --git a/assets/examples/01_layout.png b/assets/examples/01_layout.png new file mode 100644 index 0000000000000000000000000000000000000000..de36e4e8bedcbb6448375fda63b9f15e30030169 Binary files /dev/null and b/assets/examples/01_layout.png differ diff --git a/assets/examples/02_style.png b/assets/examples/02_style.png new file mode 100644 index 0000000000000000000000000000000000000000..4b209710ec03e1f51af1547ffce894a592b6a713 Binary files /dev/null and b/assets/examples/02_style.png differ diff --git a/assets/examples/03_style.png b/assets/examples/03_style.png new file mode 100644 index 0000000000000000000000000000000000000000..a84d1fd2997e5a5a15498d01835364ef74beb8e2 Binary files /dev/null and b/assets/examples/03_style.png differ diff --git a/assets/examples/04_color.png b/assets/examples/04_color.png new file mode 100644 index 0000000000000000000000000000000000000000..cc9ee62cc4c742a764c7e567b988a0716bd4cedb Binary files /dev/null and b/assets/examples/04_color.png differ diff --git a/assets/examples/05_color.png b/assets/examples/05_color.png new file mode 100644 index 0000000000000000000000000000000000000000..e7c62d94cf04a53e27effaa387a1ca76b81f2845 Binary files /dev/null and b/assets/examples/05_color.png differ diff --git a/assets/examples/06_layout.png b/assets/examples/06_layout.png new file mode 100644 index 0000000000000000000000000000000000000000..c171529bcea994b94998f0cad32680922e9f4d76 Binary files /dev/null and b/assets/examples/06_layout.png differ diff --git a/assets/examples/06_style.jpeg b/assets/examples/06_style.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..d40df1aa2d857f01ca49e31b024ad93353cf37ff Binary files /dev/null and b/assets/examples/06_style.jpeg differ diff --git a/assets/examples/07_layout.jpeg b/assets/examples/07_layout.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..af541418880609e7e95996a85652e453171ff9a4 Binary files /dev/null and b/assets/examples/07_layout.jpeg differ diff --git a/assets/examples/07_style.jpeg b/assets/examples/07_style.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..798114bb12059aee052f626440df1805ccb48536 Binary files /dev/null and b/assets/examples/07_style.jpeg differ diff --git a/assets/examples/08_color.jpeg b/assets/examples/08_color.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..9d5d592efae487e193faf522661beed3328a2d21 Binary files /dev/null and b/assets/examples/08_color.jpeg differ diff --git a/assets/examples/08_style.png b/assets/examples/08_style.png new file mode 100644 index 0000000000000000000000000000000000000000..bf7f087c395de99dc3250acc27b6ab7809bc4192 Binary files /dev/null and b/assets/examples/08_style.png differ diff --git a/assets/examples/09_base.png b/assets/examples/09_base.png new file mode 100644 index 0000000000000000000000000000000000000000..1be92d9152ea2883a5877c45904bc181df97b4e3 Binary files /dev/null and b/assets/examples/09_base.png differ diff --git a/assets/examples/09_style.png b/assets/examples/09_style.png new file mode 100644 index 0000000000000000000000000000000000000000..9180e5c3fba4a53f2914cc1b86ea9d40030bc788 Binary files /dev/null and b/assets/examples/09_style.png differ diff --git a/assets/examples/10_base.png b/assets/examples/10_base.png new file mode 100644 index 0000000000000000000000000000000000000000..51467bb839c631c94ddc6dcb29dc1c03cd072238 Binary files /dev/null and b/assets/examples/10_base.png differ diff --git a/assets/examples/10_layout.png b/assets/examples/10_layout.png new file mode 100644 index 0000000000000000000000000000000000000000..d162a841aab73752931204429aa7e4cde5c92d96 Binary files /dev/null and b/assets/examples/10_layout.png differ diff --git a/assets/examples/10_style.jpeg b/assets/examples/10_style.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..3fa97c2f6f1588b7bc392886b7ccd156791b2421 Binary files /dev/null and b/assets/examples/10_style.jpeg differ diff --git a/assets/examples/11_base.png b/assets/examples/11_base.png new file mode 100644 index 0000000000000000000000000000000000000000..967231961afdca2bb1f3386cdbfa52eb251ae0ff Binary files /dev/null and b/assets/examples/11_base.png differ diff --git a/assets/examples/11_layout.jpeg b/assets/examples/11_layout.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..f07603b33e7c785b1132456bd5f2e58430013785 Binary files /dev/null and b/assets/examples/11_layout.jpeg differ diff --git a/assets/examples/12_base.png b/assets/examples/12_base.png new file mode 100644 index 0000000000000000000000000000000000000000..0b4e5cce7149559f82394dd8f14aeadb066a8835 Binary files /dev/null and b/assets/examples/12_base.png differ diff --git a/assets/examples/12_layout.png b/assets/examples/12_layout.png new file mode 100644 index 0000000000000000000000000000000000000000..443fbd18d7d870517ea906fc0c80f5f35f0dc317 Binary files /dev/null and b/assets/examples/12_layout.png differ diff --git a/assets/examples/base_image1.jpeg b/assets/examples/base_image1.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..c1a80f97ec157a83bf6424d14166bcae7560b6d0 Binary files /dev/null and b/assets/examples/base_image1.jpeg differ diff --git a/assets/examples/base_image2.png b/assets/examples/base_image2.png new file mode 100644 index 0000000000000000000000000000000000000000..14b59004cccf331a6da1c9444601b6da09e95879 Binary files /dev/null and b/assets/examples/base_image2.png differ diff --git a/assets/examples/base_image3.png b/assets/examples/base_image3.png new file mode 100644 index 0000000000000000000000000000000000000000..0b4e5cce7149559f82394dd8f14aeadb066a8835 Binary files /dev/null and b/assets/examples/base_image3.png differ diff --git a/assets/examples/base_image4.png b/assets/examples/base_image4.png new file mode 100644 index 0000000000000000000000000000000000000000..967231961afdca2bb1f3386cdbfa52eb251ae0ff Binary files /dev/null and b/assets/examples/base_image4.png differ diff --git a/assets/examples/color_image1.jpeg b/assets/examples/color_image1.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..44ef9cedcac32eb30f7b17d5c383c621a3d95fa1 Binary files /dev/null and b/assets/examples/color_image1.jpeg differ diff --git a/assets/examples/color_image2.jpeg b/assets/examples/color_image2.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..9d5d592efae487e193faf522661beed3328a2d21 Binary files /dev/null and b/assets/examples/color_image2.jpeg differ diff --git a/assets/examples/color_image3.jpeg b/assets/examples/color_image3.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..79965613abfb2f1e289cd72b6595b3f31ce98fe2 Binary files /dev/null and b/assets/examples/color_image3.jpeg differ diff --git a/assets/examples/color_image3_mask.png b/assets/examples/color_image3_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..54bf8bd9b7ae668b69368952d41df510f98c0ee5 Binary files /dev/null and b/assets/examples/color_image3_mask.png differ diff --git a/assets/examples/empty.png b/assets/examples/empty.png new file mode 100644 index 0000000000000000000000000000000000000000..e30ec31e4e12b52e579dcf606826e8d21cb19a03 Binary files /dev/null and b/assets/examples/empty.png differ diff --git a/assets/examples/img_01.webp b/assets/examples/img_01.webp new file mode 100644 index 0000000000000000000000000000000000000000..ee124fb6c0599d9de08f41d90f1bdf6dc9710547 Binary files /dev/null and b/assets/examples/img_01.webp differ diff --git a/assets/examples/img_02.webp b/assets/examples/img_02.webp new file mode 100644 index 0000000000000000000000000000000000000000..7ef7e5086c863fd46e4a9cb1a9b22d18185414b7 Binary files /dev/null and b/assets/examples/img_02.webp differ diff --git a/assets/examples/img_03.webp b/assets/examples/img_03.webp new file mode 100644 index 0000000000000000000000000000000000000000..e10982b67c388cf3518457ecca88f0faf78c9745 Binary files /dev/null and b/assets/examples/img_03.webp differ diff --git a/assets/examples/img_04.webp b/assets/examples/img_04.webp new file mode 100644 index 0000000000000000000000000000000000000000..b73780196b99a62054cd746c3627a7152486d652 Binary files /dev/null and b/assets/examples/img_04.webp differ diff --git a/assets/examples/img_05.webp b/assets/examples/img_05.webp new file mode 100644 index 0000000000000000000000000000000000000000..62d7e628c7f1c57298d2d21d903d9d6dfb71b5e8 Binary files /dev/null and b/assets/examples/img_05.webp differ diff --git a/assets/examples/img_06.webp b/assets/examples/img_06.webp new file mode 100644 index 0000000000000000000000000000000000000000..ff75852ced132c90e30aaf0837876917aa3b5c79 Binary files /dev/null and b/assets/examples/img_06.webp differ diff --git a/assets/examples/img_07.webp b/assets/examples/img_07.webp new file mode 100644 index 0000000000000000000000000000000000000000..f1c4ba26a4d671c3705df004c5dde6b3036dbd51 Binary files /dev/null and b/assets/examples/img_07.webp differ diff --git a/assets/examples/img_08.webp b/assets/examples/img_08.webp new file mode 100644 index 0000000000000000000000000000000000000000..1c24d96ac172055e37704f2e8c3d958ec44cc256 Binary files /dev/null and b/assets/examples/img_08.webp differ diff --git a/assets/examples/img_09.webp b/assets/examples/img_09.webp new file mode 100644 index 0000000000000000000000000000000000000000..27572eb0428d3515cb3a38ba29076e0016aafc09 Binary files /dev/null and b/assets/examples/img_09.webp differ diff --git a/assets/examples/img_10.webp b/assets/examples/img_10.webp new file mode 100644 index 0000000000000000000000000000000000000000..8fe58723456c9748f9c565842c9fcc6eef7aab4d Binary files /dev/null and b/assets/examples/img_10.webp differ diff --git a/assets/examples/layout_image1.jpeg b/assets/examples/layout_image1.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..69e69e4f668b6ebe2ea68a80381d142f97d9cf0f Binary files /dev/null and b/assets/examples/layout_image1.jpeg differ diff --git a/assets/examples/layout_image1_mask.png b/assets/examples/layout_image1_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..4cfb70fcb266a15d67effb06da72748e2bc31e59 Binary files /dev/null and b/assets/examples/layout_image1_mask.png differ diff --git a/assets/examples/layout_image2.jpeg b/assets/examples/layout_image2.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..3241294d1bdb68d13f61ee6adc23bcdae5e93648 Binary files /dev/null and b/assets/examples/layout_image2.jpeg differ diff --git a/assets/examples/layout_image3.jpeg b/assets/examples/layout_image3.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..af541418880609e7e95996a85652e453171ff9a4 Binary files /dev/null and b/assets/examples/layout_image3.jpeg differ diff --git a/assets/examples/layout_image5.jpeg b/assets/examples/layout_image5.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..e4da3d1618f8c301adb1a1e8b1efd78be34c4d87 Binary files /dev/null and b/assets/examples/layout_image5.jpeg differ diff --git a/assets/examples/layout_image5_mask.png b/assets/examples/layout_image5_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..475405cea1a756ccfe257b725530c067f1213b30 Binary files /dev/null and b/assets/examples/layout_image5_mask.png differ diff --git a/assets/examples/layout_image6.jpeg b/assets/examples/layout_image6.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..cd2e756e22806c20a7ab759db2d4cbb5beedb177 Binary files /dev/null and b/assets/examples/layout_image6.jpeg differ diff --git a/assets/examples/layout_image6_mask.png b/assets/examples/layout_image6_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..b41b47524f3cd0bc4905df9612d4cd8d6d0322e2 Binary files /dev/null and b/assets/examples/layout_image6_mask.png differ diff --git a/assets/examples/layout_image7.jpeg b/assets/examples/layout_image7.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..e7ca9339a5096e75773a1580236b692ccdca741c Binary files /dev/null and b/assets/examples/layout_image7.jpeg differ diff --git a/assets/examples/oTtfLXcVnPgtDWRYK7gC--1--dfkli.webp b/assets/examples/oTtfLXcVnPgtDWRYK7gC--1--dfkli.webp new file mode 100644 index 0000000000000000000000000000000000000000..77f147859a54ef645e3f5c064d1fc1f06b161088 Binary files /dev/null and b/assets/examples/oTtfLXcVnPgtDWRYK7gC--1--dfkli.webp differ diff --git a/assets/examples/style_image1.jpeg b/assets/examples/style_image1.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..c76b17b4ce63ee30395730de1b8ff6e158fa1c14 Binary files /dev/null and b/assets/examples/style_image1.jpeg differ diff --git a/assets/examples/style_image1_mask.png b/assets/examples/style_image1_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..4ce5f11cfc35f205f6377a3eee5b93a4c8508b5f Binary files /dev/null and b/assets/examples/style_image1_mask.png differ diff --git a/assets/examples/style_image1_mask2.png b/assets/examples/style_image1_mask2.png new file mode 100644 index 0000000000000000000000000000000000000000..3a587ca73dfcaac09600c044402ff044214e43e9 Binary files /dev/null and b/assets/examples/style_image1_mask2.png differ diff --git a/assets/examples/style_image2.jpeg b/assets/examples/style_image2.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..d40df1aa2d857f01ca49e31b024ad93353cf37ff Binary files /dev/null and b/assets/examples/style_image2.jpeg differ diff --git a/assets/examples/style_image3.jpeg b/assets/examples/style_image3.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..798114bb12059aee052f626440df1805ccb48536 Binary files /dev/null and b/assets/examples/style_image3.jpeg differ diff --git a/assets/examples/style_image4.jpeg b/assets/examples/style_image4.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..09002315425bbc18c94fa170c3186b1c89d688f9 Binary files /dev/null and b/assets/examples/style_image4.jpeg differ diff --git a/assets/examples/style_image5.jpeg b/assets/examples/style_image5.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..d9c7b132195e365f3fec4deb06b792d0c19f46e0 Binary files /dev/null and b/assets/examples/style_image5.jpeg differ diff --git a/assets/examples/style_image6.jpeg b/assets/examples/style_image6.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..c5f2f0001c2801787f35c6a9ff2206e7b9b366d8 Binary files /dev/null and b/assets/examples/style_image6.jpeg differ diff --git a/assets/examples/style_image6_mask.png b/assets/examples/style_image6_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..a03176bdfdcad6d772e5cfccbf1a604659a1fd21 Binary files /dev/null and b/assets/examples/style_image6_mask.png differ diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/generation.py b/src/generation.py new file mode 100644 index 0000000000000000000000000000000000000000..3e2e2100bcda8e90c271aec148218deb49c63fa8 --- /dev/null +++ b/src/generation.py @@ -0,0 +1,101 @@ +import json +import os +import time + +import gradio as gr +import requests + +from src.log import logger +from src.util import download_images + + +def call_generation(base_image_url="", layout_image_url="", color_image_url="", style_image_url="", + strict_edge=0, layout_scale=8, edge_consistency=8, color_scale=8, + style_scale=8, prompt="", negative_prompt="", output_aspect_ratio=1.0): + API_KEY = os.getenv("API_KEY_BG_GENERATION") + if output_aspect_ratio >= 1: + BATCH_SIZE = 2 + REPEAT = 2 + else: + BATCH_SIZE = 4 + REPEAT = 1 + headers = { + "Content-Type": "application/json", + "Accept": "application/json", + "Authorization": f"Bearer {API_KEY}", + "X-DashScope-Async": "enable", + } + data = { + "model": "wanx-poster-imitation-v1", + "input": { + "base_image_url": base_image_url, + "layout_image_url": layout_image_url, + "color_image_url": color_image_url, + "style_image_url": style_image_url, + "prompt": prompt, + "negative_prompt": negative_prompt, + }, + "parameters": { + "strict_layout": strict_edge, + "layout_scale": layout_scale, + "layout_spatial_consistency": edge_consistency, + "color_scale": color_scale, + "style_scale": style_scale, + "n": BATCH_SIZE + } + } + url_create_task = 'https://dashscope.aliyuncs.com/api/v1/services/aigc/poster-imitation/generation' + + all_res_ = [] + for _ in range(REPEAT): + res_ = requests.post(url_create_task, data=json.dumps(data), headers=headers) + all_res_.append(res_) + + all_image_data = [] + for res_ in all_res_: + respose_code = res_.status_code + if 200 == respose_code: + res = json.loads(res_.content.decode()) + request_id = res['request_id'] + task_id = res['output']['task_id'] + logger.info(f"task_id: {task_id}: Create Poster Imitation request success. Params: {data}") + + # 异步查询 + is_running = True + while is_running: + url_query = f'https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}' + res_ = requests.post(url_query, headers=headers) + respose_code = res_.status_code + if 200 == respose_code: + res = json.loads(res_.content.decode()) + if "SUCCEEDED" == res['output']['task_status']: + logger.info(f"task_id: {task_id}: Generation task query success.") + results = res['output']['results'] + img_urls = [x['url'] for x in results] + logger.info(f"task_id: {task_id}: {res}") + break + elif "FAILED" != res['output']['task_status']: + logger.debug(f"task_id: {task_id}: query result...") + time.sleep(1) + else: + raise gr.Error('Fail to get results from Generation task.') + + else: + logger.error(f'task_id: {task_id}: Fail to query task result: {res_.content}') + raise gr.Error("Fail to query task result.") + + logger.info(f"task_id: {task_id}: download generated images.") + img_data = download_images(img_urls, BATCH_SIZE) + logger.info(f"task_id: {task_id}: Generate done.") + all_image_data += img_data + else: + logger.error(f'Fail to create Generation task: {res_.content}') + raise gr.Error("Fail to create Generation task.") + + if len(all_image_data) != REPEAT * BATCH_SIZE: + raise gr.Error("Fail to Generation.") + return all_image_data + + +if __name__ == "__main__": + call_generation() diff --git a/src/log.py b/src/log.py new file mode 100644 index 0000000000000000000000000000000000000000..044070382a8577aea1ec5f05d3d7681ae94feff1 --- /dev/null +++ b/src/log.py @@ -0,0 +1,18 @@ +import logging +import os +from logging.handlers import RotatingFileHandler + +log_file_name = "workdir/log_transferAnything.log" +os.makedirs(os.path.dirname(log_file_name), exist_ok=True) + +format = '[%(levelname)s] %(asctime)s "%(filename)s", line %(lineno)d, %(message)s' +logging.basicConfig( + format=format, + datefmt="%Y-%m-%d %H:%M:%S", + level=logging.INFO) +logger = logging.getLogger(name="WordArt_Studio") + +fh = RotatingFileHandler(log_file_name, maxBytes=20000000, backupCount=3) +formatter = logging.Formatter(format, datefmt="%Y-%m-%d %H:%M:%S") +fh.setFormatter(formatter) +logger.addHandler(fh) diff --git a/src/util.py b/src/util.py new file mode 100644 index 0000000000000000000000000000000000000000..5b8bb9deea307659f76088e0770c86392b98c6de --- /dev/null +++ b/src/util.py @@ -0,0 +1,251 @@ +import concurrent.futures +import io +import os + +import numpy as np +import oss2 +import requests +from PIL import Image, ImageDraw, ImageFont + +from .log import logger + +# oss +access_key_id = os.getenv("ACCESS_KEY_ID") +access_key_secret = os.getenv("ACCESS_KEY_SECRET") +bucket_name = os.getenv("BUCKET_NAME") +endpoint = os.getenv("ENDPOINT") + +bucket = oss2.Bucket(oss2.Auth(access_key_id, access_key_secret), endpoint, bucket_name) +oss_path = "hejunjie.hjj/TransferAnythingHF" +oss_path_img_gallery = "hejunjie.hjj/TransferAnythingHF_img_gallery" + + +def download_img_pil(index, img_url): + # print(img_url) + r = requests.get(img_url, stream=True) + if r.status_code == 200: + img = Image.open(io.BytesIO(r.content)) + return (index, img) + else: + logger.error(f"Fail to download: {img_url}") + + +def download_images(img_urls, batch_size): + imgs_pil = [None] * batch_size + # worker_results = [] + with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor: + to_do = [] + for i, url in enumerate(img_urls): + future = executor.submit(download_img_pil, i, url) + to_do.append(future) + + for future in concurrent.futures.as_completed(to_do): + ret = future.result() + # worker_results.append(ret) + index, img_pil = ret + imgs_pil[index] = img_pil # 按顺序排列url,后续下载关联的图片或者svg需要使用 + + return imgs_pil + + +def upload_np_2_oss(input_image, name="cache.png", gallery=False): + assert name.lower().endswith((".png", ".jpg")), name + imgByteArr = io.BytesIO() + if name.lower().endswith(".png"): + Image.fromarray(input_image).save(imgByteArr, format="PNG") + else: + Image.fromarray(input_image).save(imgByteArr, format="JPEG", quality=95) + imgByteArr = imgByteArr.getvalue() + + if gallery: + path = oss_path_img_gallery + else: + path = oss_path + + bucket.put_object(path + "/" + name, imgByteArr) # data为数据,可以是图片 + ret = bucket.sign_url('GET', path + "/" + name, 60 * 60 * 24) # 返回值为链接,参数依次为,方法/oss上文件路径/过期时间(s) + del imgByteArr + return ret + + +def upload_json_string_2_oss(jsonStr, name="cache.txt", gallery=False): + if gallery: + path = oss_path_img_gallery + else: + path = oss_path + + bucket.put_object(path + "/" + name, bytes(jsonStr, "utf-8")) # data为数据 + ret = bucket.sign_url('GET', path + "/" + name, 60 * 60 * 24) # 返回值为链接,参数依次为,方法/oss上文件路径/过期时间(s) + return ret + + +def upload_preprocess(pil_base_image_rgba, pil_layout_image_dict, pil_style_image_dict, pil_color_image_dict, + pil_fg_mask): + np_out_base_image = np_out_layout_image = np_out_style_image = np_out_color_image = None + + if pil_base_image_rgba is not None: + np_fg_image = np.array(pil_base_image_rgba)[..., :3] + np_fg_mask = np.expand_dims(np.array(pil_fg_mask).astype(float), axis=-1) / 255. + np_fg_mask = np_fg_mask * 0.5 + 0.5 + np_out_base_image = (np_fg_image * np_fg_mask + (1 - np_fg_mask) * np.array([0, 0, 255])).round().clip(0, + 255).astype( + np.uint8) + + if pil_layout_image_dict is not None: + np_layout_image = np.array(pil_layout_image_dict["image"].convert("RGBA")) + np_layout_image, np_layout_alpha = np_layout_image[..., :3], np_layout_image[..., 3] + np_layout_mask = np.array(pil_layout_image_dict["mask"].convert("L")) + np_layout_mask = ((np_layout_alpha > 127) * (np_layout_mask < 127)).astype(float)[..., None] + np_layout_mask = np_layout_mask * 0.5 + 0.5 + np_out_layout_image = ( + np_layout_image * np_layout_mask + (1 - np_layout_mask) * np.array([0, 0, 255])).round().clip(0, + 255).astype( + np.uint8) + + if pil_style_image_dict is not None: + np_style_image = np.array(pil_style_image_dict["image"].convert("RGBA")) + np_style_image, np_style_alpha = np_style_image[..., :3], np_style_image[..., 3] + np_style_mask = np.array(pil_style_image_dict["mask"].convert("L")) + np_style_mask = ((np_style_alpha > 127) * (np_style_mask < 127)).astype(float)[..., None] + np_style_mask = np_style_mask * 0.5 + 0.5 + np_out_style_image = ( + np_style_image * np_style_mask + (1 - np_style_mask) * np.array([0, 0, 255])).round().clip(0, + 255).astype( + np.uint8) + + if pil_color_image_dict is not None: + np_color_image = np.array(pil_color_image_dict["image"].convert("RGBA")) + np_color_image, np_color_alpha = np_color_image[..., :3], np_color_image[..., 3] + np_color_mask = np.array(pil_color_image_dict["mask"].convert("L")) + np_color_mask = ((np_color_alpha > 127) * (np_color_mask < 127)).astype(float)[..., None] + np_color_mask = np_color_mask * 0.5 + 0.5 + np_out_color_image = ( + np_color_image * np_color_mask + (1 - np_color_mask) * np.array([0, 0, 255])).round().clip(0, + 255).astype( + np.uint8) + + return np_out_base_image, np_out_layout_image, np_out_style_image, np_out_color_image + + +def pad_image(image, target_size): + iw, ih = image.size # 原始图像的尺寸 + w, h = target_size # 目标图像的尺寸 + scale = min(w / iw, h / ih) # 转换的最小比例 + # 保证长或宽,至少一个符合目标图像的尺寸 0.5保证四舍五入 + nw = int(iw * scale + 0.5) + nh = int(ih * scale + 0.5) + image = image.resize((nw, nh), Image.BICUBIC) # 更改图像尺寸,双立法插值效果很好 + new_image = Image.new('RGB', target_size, (255, 255, 255)) # 生成白色图像 + new_image.paste(image, ((w - nw) // 2, (h - nh) // 2)) # 将图像填充为中间图像,两侧为黑色的样式 + return new_image + + +def add_text(image, text): + w, h = image.size + text_image = image.copy() + text_image_draw = ImageDraw.Draw(text_image) + + ttf = ImageFont.truetype("assets/ttf/AlibabaPuHuiTi-2-55-Regular.ttf", int(h / 10)) + left, top, right, bottom = ttf.getbbox(text) + text_image_draw.rectangle((0, 0, right + left, bottom + top), fill=(255, 255, 255)) + + image = Image.blend(image, text_image, 0.5) + + image_draw = ImageDraw.Draw(image) + fillColor = (0, 0, 0, 255) # 文字颜色:黑色 + pos = (0, 0) # 文本左上角位置 (离左边界距离, 离上边界距离) + image_draw.text(pos, text, font=ttf, fill=fillColor) + return image.convert("RGB") + + +def compose_image(image_list, text_list, pil_size, nrow, ncol): + w, h = pil_size # 每张小图片大小 + + if len(image_list) > nrow * ncol: + raise ValueError("合成图片的参数和要求的数量不能匹配!") + + assert len(image_list) == len(text_list) + new_image_list = [] + new_text_list = [] + for image, text in zip(image_list, text_list): + if image is not None: + new_image_list.append(image) + new_text_list.append(text) + if len(new_image_list) == 1: + ncol = nrow = 1 + to_image = Image.new('RGB', (ncol * w, nrow * h), (255, 255, 255)) # 创建一个新图 + for y in range(1, nrow + 1): + for x in range(1, ncol + 1): + if ncol * (y - 1) + x - 1 < len(new_image_list): + from_image = new_image_list[ncol * (y - 1) + x - 1].resize((w, h), Image.BICUBIC) + from_text = new_text_list[ncol * (y - 1) + x - 1] + if from_text is not None: + from_image = add_text(from_image, from_text) + to_image.paste(from_image, ((x - 1) * w, (y - 1) * h)) + return to_image + + +def split_text_lines(text, max_w, ttf): + text_split_lines = [] + text_h = 0 + if text != "": + line_start = 0 + while line_start < len(text): + line_count = 0 + _, _, right, bottom = ttf.getbbox(text[line_start: line_start + line_count + 1]) + while right < max_w and line_count < len(text): + line_count += 1 + _, _, right, bottom = ttf.getbbox(text[line_start: line_start + line_count + 1]) + text_split_lines.append(text[line_start:line_start + line_count]) + text_h += bottom + line_start += line_count + return text_split_lines, text_h + + +def add_prompt(image, prompt, negative_prompt): + if prompt == "" and negative_prompt == "": + return image + if prompt != "": + prompt = "Prompt: " + prompt + if negative_prompt != "": + negative_prompt = "Negative prompt: " + negative_prompt + + w, h = image.size + + ttf = ImageFont.truetype("assets/ttf/AlibabaPuHuiTi-2-55-Regular.ttf", int(h / 20)) + + prompt_split_lines, prompt_h = split_text_lines(prompt, w, ttf) + negative_prompt_split_lines, negative_prompt_h = split_text_lines(negative_prompt, w, ttf) + text_h = prompt_h + negative_prompt_h + text = "\n".join(prompt_split_lines + negative_prompt_split_lines) + text_image = Image.new(image.mode, (w, text_h), color=(255, 255, 255)) + text_image_draw = ImageDraw.Draw(text_image) + text_image_draw.text((0, 0), text, font=ttf, fill=(0, 0, 0)) + + out_image = Image.new(image.mode, (w, h + text_h), color=(255, 255, 255)) + out_image.paste(image, (0, 0)) + out_image.paste(text_image, (0, h)) + + return out_image + + +def merge_images(np_fg_image, np_layout_image, np_style_image, np_color_image, np_res_image, prompt, negative_prompt): + pil_res_image = Image.fromarray(np_res_image) + + w, h = pil_res_image.size + pil_fg_image = None if np_fg_image is None else pad_image(Image.fromarray(np_fg_image), (w, h)) + pil_layout_image = None if np_layout_image is None else pad_image(Image.fromarray(np_layout_image), (w, h)) + pil_style_image = None if np_style_image is None else pad_image(Image.fromarray(np_style_image), (w, h)) + pil_color_image = None if np_color_image is None else pad_image(Image.fromarray(np_color_image), (w, h)) + + input_images = [pil_layout_image, pil_style_image, pil_color_image, pil_fg_image] + input_texts = ['Layout', 'Style', 'Color', 'Subject'] + input_compose_image = compose_image(input_images, input_texts, (w, h), nrow=2, ncol=2) + input_compose_image = input_compose_image.resize((w, h), Image.BICUBIC) + output_compose_image = compose_image([input_compose_image, pil_res_image], [None, None], (w, h), nrow=1, + ncol=2) + output_compose_image = add_prompt(output_compose_image, prompt, negative_prompt) + + output_compose_image = np.array(output_compose_image) + + return output_compose_image