CoolFace
Apppublic

MykolaL/StableDesign

sourceHugging Facemitupdated 4mo agoView on Hugging Face
120likes
app.py378 linesDownload Raw Back to root
1import spaces2from typing import Tuple, Union, List3import os4 5import numpy as np6from PIL import Image7 8import torch9from diffusers.pipelines.controlnet import StableDiffusionControlNetInpaintPipeline10from diffusers import ControlNetModel, UniPCMultistepScheduler, AutoPipelineForText2Image11from transformers import AutoImageProcessor, UperNetForSemanticSegmentation, AutoModelForDepthEstimation12from colors import ade_palette13from utils import map_colors_rgb14from diffusers import StableDiffusionXLPipeline15import gradio as gr16import gc17 18device = "cuda"19dtype = torch.float1620 21        22css = """23#img-display-container {24    max-height: 50vh;25    }26#img-display-input {27    max-height: 40vh;28    }29#img-display-output {30    max-height: 40vh;31    }32 33"""34 35 36def filter_items(37    colors_list: Union[List, np.ndarray],38    items_list: Union[List, np.ndarray],39    items_to_remove: Union[List, np.ndarray]40) -> Tuple[Union[List, np.ndarray], Union[List, np.ndarray]]:41    """42    Filters items and their corresponding colors from given lists, excluding43    specified items.44 45    Args:46        colors_list: A list or numpy array of colors corresponding to items.47        items_list: A list or numpy array of items.48        items_to_remove: A list or numpy array of items to be removed.49 50    Returns:51        A tuple of two lists or numpy arrays: filtered colors and filtered52        items.53    """54    filtered_colors = []55    filtered_items = []56    for color, item in zip(colors_list, items_list):57        if item not in items_to_remove:58            filtered_colors.append(color)59            filtered_items.append(item)60    return filtered_colors, filtered_items61 62def get_segmentation_pipeline(63) -> Tuple[AutoImageProcessor, UperNetForSemanticSegmentation]:64    """Method to load the segmentation pipeline65    Returns:66        Tuple[AutoImageProcessor, UperNetForSemanticSegmentation]: segmentation pipeline67    """68    image_processor = AutoImageProcessor.from_pretrained(69        "openmmlab/upernet-convnext-small"70    )71    image_segmentor = UperNetForSemanticSegmentation.from_pretrained(72        "openmmlab/upernet-convnext-small"73    )74    return image_processor, image_segmentor75 76 77@torch.inference_mode()78@spaces.GPU79def segment_image(80        image: Image,81        image_processor: AutoImageProcessor,82        image_segmentor: UperNetForSemanticSegmentation83) -> Image:84    """85    Segments an image using a semantic segmentation model.86 87    Args:88        image (Image): The input image to be segmented.89        image_processor (AutoImageProcessor): The processor to prepare the90            image for segmentation.91        image_segmentor (UperNetForSemanticSegmentation): The semantic92            segmentation model used to identify different segments in the image.93 94    Returns:95        Image: The segmented image with each segment colored differently based96            on its identified class.97    """98    # image_processor, image_segmentor = get_segmentation_pipeline()99    pixel_values = image_processor(image, return_tensors="pt").pixel_values100    with torch.no_grad():101        outputs = image_segmentor(pixel_values)102 103    seg = image_processor.post_process_semantic_segmentation(104        outputs, target_sizes=[image.size[::-1]])[0]105    color_seg = np.zeros((seg.shape[0], seg.shape[1], 3), dtype=np.uint8)106    palette = np.array(ade_palette())107    for label, color in enumerate(palette):108        color_seg[seg == label, :] = color109    color_seg = color_seg.astype(np.uint8)110    seg_image = Image.fromarray(color_seg).convert('RGB')111    return seg_image112 113 114def get_depth_pipeline():115    feature_extractor = AutoImageProcessor.from_pretrained("LiheYoung/depth-anything-large-hf",116                                                           torch_dtype=dtype)117    depth_estimator = AutoModelForDepthEstimation.from_pretrained("LiheYoung/depth-anything-large-hf",118                                                                  torch_dtype=dtype)119    return feature_extractor, depth_estimator120 121 122@torch.inference_mode()123@spaces.GPU124def get_depth_image(125        image: Image,126        feature_extractor: AutoImageProcessor,127        depth_estimator: AutoModelForDepthEstimation128) -> Image:129    image_to_depth = feature_extractor(images=image, return_tensors="pt").to(device)130    with torch.no_grad():131        depth_map = depth_estimator(**image_to_depth).predicted_depth132 133    width, height = image.size134    depth_map = torch.nn.functional.interpolate(135        depth_map.unsqueeze(1).float(),136        size=(height, width),137        mode="bicubic",138        align_corners=False,139    )140    depth_min = torch.amin(depth_map, dim=[1, 2, 3], keepdim=True)141    depth_max = torch.amax(depth_map, dim=[1, 2, 3], keepdim=True)142    depth_map = (depth_map - depth_min) / (depth_max - depth_min)143    image = torch.cat([depth_map] * 3, dim=1)144 145    image = image.permute(0, 2, 3, 1).cpu().numpy()[0]146    image = Image.fromarray((image * 255.0).clip(0, 255).astype(np.uint8))147    return image148 149 150def resize_dimensions(dimensions, target_size):151    """ 152    Resize PIL to target size while maintaining aspect ratio 153    If smaller than target size leave it as is154    """155    width, height = dimensions156 157    # Check if both dimensions are smaller than the target size158    if width < target_size and height < target_size:159        return dimensions160 161    # Determine the larger side162    if width > height:163        # Calculate the aspect ratio164        aspect_ratio = height / width165        # Resize dimensions166        return (target_size, int(target_size * aspect_ratio))167    else:168        # Calculate the aspect ratio169        aspect_ratio = width / height170        # Resize dimensions171        return (int(target_size * aspect_ratio), target_size)172 173 174def flush():175    gc.collect()176    torch.cuda.empty_cache()177    178    179class ControlNetDepthDesignModelMulti:180    """ Produces random noise images """181    182    def __init__(self):183        """ Initialize your model(s) here """184        #os.environ['HF_HUB_OFFLINE'] = "True"185        186        self.seed = 323*111187        self.neg_prompt = "window, door, low resolution, banner, logo, watermark, text, deformed, blurry, out of focus, surreal, ugly, beginner"188        self.control_items = ["windowpane;window", "door;double;door"]189        self.additional_quality_suffix = "interior design, 4K, high resolution, photorealistic"190        191    @spaces.GPU192    def generate_design(self, empty_room_image: Image, prompt: str, guidance_scale: int = 10, num_steps: int = 50, strength: float =0.9, img_size: int = 640) -> Image:193        """194        Given an image of an empty room and a prompt195        generate the designed room according to the prompt196        Inputs - 197            empty_room_image - An RGB PIL Image of the empty room198            prompt - Text describing the target design elements of the room199        Returns - 200            design_image - PIL Image of the same size as the empty room image201                           If the size is not the same the submission will fail.202        """203        print(prompt)204        flush()205        self.generator = torch.Generator(device=device).manual_seed(self.seed)206 207        pos_prompt = prompt + f', {self.additional_quality_suffix}'208 209        orig_w, orig_h = empty_room_image.size210        new_width, new_height = resize_dimensions(empty_room_image.size, img_size)211        input_image = empty_room_image.resize((new_width, new_height))212        real_seg = np.array(segment_image(input_image,213                                          seg_image_processor,214                                          image_segmentor))215        unique_colors = np.unique(real_seg.reshape(-1, real_seg.shape[2]), axis=0)216        unique_colors = [tuple(color) for color in unique_colors]217        segment_items = [map_colors_rgb(i) for i in unique_colors]218        chosen_colors, segment_items = filter_items(219            colors_list=unique_colors,220            items_list=segment_items,221            items_to_remove=self.control_items222        )223        mask = np.zeros_like(real_seg)224        for color in chosen_colors:225            color_matches = (real_seg == color).all(axis=2)226            mask[color_matches] = 1227 228        image_np = np.array(input_image)229        image = Image.fromarray(image_np).convert("RGB")230        mask_image = Image.fromarray((mask * 255).astype(np.uint8)).convert("RGB")231        segmentation_cond_image = Image.fromarray(real_seg).convert("RGB")232 233        image_depth = get_depth_image(image, depth_feature_extractor, depth_estimator)234 235        # generate image that would be used as IP-adapter236        flush()237        new_width_ip = int(new_width / 8) * 8238        new_height_ip = int(new_height / 8) * 8239        ip_image = guide_pipe(pos_prompt,240                                   num_inference_steps=num_steps,241                                   negative_prompt=self.neg_prompt,242                                   height=new_height_ip,243                                   width=new_width_ip,244                                   generator=[self.generator]).images[0]245 246        flush()247        generated_image = pipe(248            prompt=pos_prompt,249            negative_prompt=self.neg_prompt,250            num_inference_steps=num_steps,251            strength=strength,252            guidance_scale=guidance_scale,253            generator=[self.generator],254            image=image,255            mask_image=mask_image,256            ip_adapter_image=ip_image,257            control_image=[image_depth, segmentation_cond_image],258            controlnet_conditioning_scale=[0.5, 0.5]259        ).images[0]260        261        flush()262        design_image = generated_image.resize(263            (orig_w, orig_h), Image.Resampling.LANCZOS264        )265        266        return design_image267 268 269def create_demo(model):270    gr.Markdown("### Stable Design demo")271    with gr.Row():272        with gr.Column():273            input_image = gr.Image(label="Input Image", type='pil', elem_id='img-display-input')274            input_text = gr.Textbox(label='Prompt', placeholder='Please upload your image first', lines=2)275            with gr.Accordion('Advanced options', open=False):276                num_steps = gr.Slider(label='Steps',277                                      minimum=1,278                                      maximum=50,279                                      value=50,280                                      step=1)281                img_size = gr.Slider(label='Image size',282                                      minimum=256,283                                      maximum=768,284                                      value=768,285                                      step=64)286                guidance_scale = gr.Slider(label='Guidance Scale',287                                           minimum=0.1,288                                           maximum=30.0,289                                           value=10.0,290                                           step=0.1)291                seed = gr.Slider(label='Seed',292                                 minimum=-1,293                                 maximum=2147483647,294                                 value=323*111,295                                 step=1,296                                 randomize=True)297                strength = gr.Slider(label='Strength',298                                           minimum=0.1,299                                           maximum=1.0,300                                           value=0.9,301                                           step=0.1)302                a_prompt = gr.Textbox(303                    label='Added Prompt',304                    value="interior design, 4K, high resolution, photorealistic")305                n_prompt = gr.Textbox(306                    label='Negative Prompt',307                    value="window, door, low resolution, banner, logo, watermark, text, deformed, blurry, out of focus, surreal, ugly, beginner")308            submit = gr.Button("Submit")309        310        with gr.Column():311            design_image = gr.Image(label="Output Mask", elem_id='img-display-output')312    313    314    def on_submit(image, text, num_steps, guidance_scale, seed, strength, a_prompt, n_prompt, img_size):315        model.seed = seed316        model.neg_prompt = n_prompt317        model.additional_quality_suffix = a_prompt318        319        with torch.no_grad():320            out_img = model.generate_design(image, text, guidance_scale=guidance_scale, num_steps=num_steps, strength=strength, img_size=img_size)321 322        return out_img323 324    submit.click(on_submit, inputs=[input_image, input_text, num_steps, guidance_scale, seed, strength, a_prompt, n_prompt, img_size], outputs=design_image)325    examples = gr.Examples(examples=[["imgs/bedroom_1.jpg", "An elegantly appointed bedroom in the Art Deco style, featuring a grand king-size bed with geometric bedding, a luxurious velvet armchair, and a mirrored nightstand that reflects the room's opulence. Art Deco-inspired artwork adds a touch of glamour"], ["imgs/bedroom_2.jpg", "A bedroom that exudes French country charm with a soft upholstered bed, walls adorned with floral wallpaper, and a vintage wooden wardrobe. A crystal chandelier casts a warm, inviting glow over the space"], ["imgs/dinning_room_1.jpg", "A cozy dining room that captures the essence of rustic charm with a solid wooden farmhouse table at its core, surrounded by an eclectic mix of mismatched chairs. An antique sideboard serves as a statement piece, and the ambiance is warmly lit by a series of quaint Edison bulbs dangling from the ceiling"], ["imgs/dinning_room_3.jpg", "A dining room that epitomizes contemporary elegance, anchored by a sleek, minimalist dining table paired with stylish modern chairs. Artistic lighting fixtures create a focal point above, while the surrounding minimalist decor ensures the space feels open, airy, and utterly modern"], ["imgs/image_1.jpg", "A glamorous master bedroom in Hollywood Regency style, boasting a plush tufted headboard, mirrored furniture reflecting elegance, luxurious fabrics in rich textures, and opulent gold accents for a touch of luxury."], ["imgs/image_2.jpg", "A vibrant living room with a tropical theme, complete with comfortable rattan furniture, large leafy plants bringing the outdoors in, bright cushions adding pops of color, and bamboo blinds for natural light control."], ["imgs/living_room_1.jpg", "A stylish living room embracing mid-century modern aesthetics, featuring a vintage teak coffee table at its center, complemented by a classic sunburst clock on the wall and a cozy shag rug underfoot, creating a warm and inviting atmosphere"]],326                           inputs=[input_image, input_text], cache_examples=False)327 328 329controlnet_depth= ControlNetModel.from_pretrained(330    "controlnet_depth", torch_dtype=dtype, use_safetensors=True)331controlnet_seg = ControlNetModel.from_pretrained(332    "own_controlnet", torch_dtype=dtype, use_safetensors=True)333 334pipe = StableDiffusionControlNetInpaintPipeline.from_pretrained(335    "SG161222/Realistic_Vision_V5.1_noVAE",336    #"models/runwayml--stable-diffusion-inpainting",337    controlnet=[controlnet_depth, controlnet_seg],338    safety_checker=None,339    torch_dtype=dtype340)341 342pipe.load_ip_adapter("h94/IP-Adapter", subfolder="models",343                     weight_name="ip-adapter_sd15.bin")344pipe.set_ip_adapter_scale(0.4)345pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.config)346pipe = pipe.to(device)347guide_pipe = StableDiffusionXLPipeline.from_pretrained("segmind/SSD-1B",348                                                       torch_dtype=dtype, use_safetensors=True, variant="fp16")349guide_pipe = guide_pipe.to(device)350   351seg_image_processor, image_segmentor = get_segmentation_pipeline()352depth_feature_extractor, depth_estimator = get_depth_pipeline()353depth_estimator = depth_estimator.to(device)354 355 356def main():357    model = ControlNetDepthDesignModelMulti()358    print('Models uploaded successfully')359    360    title = "# StableDesign"361    description = """362    <p style='font-size: 14px; margin-bottom: 10px;'><a href='https://www.linkedin.com/in/mykola-lavreniuk/'>Mykola Lavreniuk</a>, <a href='https://www.linkedin.com/in/bartosz-ludwiczuk-a677a760/'>Bartosz Ludwiczuk</a></p>363    <p style='font-size: 16px; margin-bottom: 0px; margin-top=0px;'>Official demo for <strong>StableDesign:</strong> 2nd place solution for the Generative Interior Design 2024 <a href='https://www.aicrowd.com/challenges/generative-interior-design-challenge-2024/leaderboards?challenge_round_id=1314'>competition</a>. StableDesign is a deep learning model designed to harness the power of AI, providing innovative and creative tools for designers. Using our algorithms, images of empty rooms can be transformed into fully furnished spaces based on text descriptions. Please refer to our <a href='https://github.com/Lavreniuk/generative-interior-design'>GitHub</a> for more details.</p>364    """365    with gr.Blocks() as demo:366        gr.Markdown(title)367        gr.Markdown(description)368        369        create_demo(model)370        gr.HTML('''<br><br><br><center>You can duplicate this Space to skip the queue:<a href="https://huggingface.co/spaces/MykolaL/StableDesign?duplicate=true"><img src="https://bit.ly/3gLdBN6" alt="Duplicate Space"></a><br>371                <p><img src="https://visitor-badge.glitch.me/badge?page_id=MykolaL/StableDesign" alt="visitors"></p></center>''')372 373    demo.queue().launch(share=False)374 375 376if __name__ == '__main__':377    main()378