File size: 11,883 Bytes
e0249e4
 
 
b6e7a04
5610674
1b52846
 
 
 
53e99a4
 
 
7735f13
 
53e99a4
9062672
8c99500
064c240
 
8c99500
 
 
 
 
 
 
 
 
9062672
1b52846
 
 
 
 
 
 
 
572bbf8
d256021
579c825
d256021
 
572bbf8
53e99a4
 
ba6f1ac
53e99a4
 
 
e0249e4
572bbf8
ba6f1ac
 
 
a1d70a2
 
ba6f1ac
 
a1d70a2
 
ba6f1ac
 
 
a1d70a2
572bbf8
c79550e
ba6f1ac
 
e0249e4
 
 
 
 
b6e7a04
 
 
5a52a84
1b52846
 
 
addc1a9
1b52846
 
 
 
 
 
 
5a52a84
 
53e99a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ba6f1ac
5a52a84
e81ae3c
ba6f1ac
9e4c4f0
6487748
ba6f1ac
 
6487748
e81ae3c
ba6f1ac
 
5a52a84
ba6f1ac
 
 
 
 
9e4c4f0
ba6f1ac
addc1a9
ba6f1ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5a52a84
b6e7a04
 
 
 
 
 
 
 
5a52a84
b6e7a04
 
 
 
 
5610674
5a52a84
e81ae3c
5610674
 
767f2af
dcd81d4
 
f2f5d76
 
86aca84
f2f5d76
e81ae3c
86aca84
 
7735f13
 
88d0708
5a52a84
 
9c17756
86aca84
f2f5d76
88d0708
f2f5d76
88d0708
 
 
 
86aca84
 
 
 
 
 
 
 
 
 
 
 
 
7735f13
 
86aca84
 
5610674
b6e7a04
5a52a84
b6e7a04
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a9c195c
 
 
 
 
e81ae3c
 
b4d9f84
 
5486c62
b4d9f84
5486c62
b4d9f84
 
a9c195c
f93edf6
b6e7a04
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21f3825
 
 
 
 
5610674
 
e81ae3c
a9e2dad
5610674
b6e7a04
86aca84
9c17756
86aca84
b6e7a04
 
 
21f3825
 
 
 
 
 
b6e7a04
 
a50535c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
import gradio as gr
import spaces
import torch
import os
import gradio as gr
import cv2
import numpy as np
from diffusers.utils import load_image 
from PIL import Image
import torch
from transformers import AutoProcessor, AutoModelForCausalLM
from diffusers import ControlNetModel, StableDiffusionControlNetPipeline, UniPCMultistepScheduler
import gc
import torch

# Programmatically add the repository to PyTorch Hub's trusted list
#torch.hub.set_dir('/root/.cache/torch/hub') # Optional: ensures clean cache path
#torch.hub.trusted_list.append("rwightman/gen-efficientnet-pytorch")
#torch.hub.trusted_list.append("intel-isl/MiDaS")
import torch.hub

# Globally override the default 'check' state to always force allow downloads
orig_load = torch.hub.load
def patched_load(*args, **kwargs):
    kwargs['trust_repo'] = True
    return orig_load(*args, **kwargs)

torch.hub.load = patched_load


device = "cuda" if torch.cuda.is_available() else "cpu"
torch_dtype = torch.float16 if torch.cuda.is_available() else torch.float32

print("Initializing Models (Florence-2 & )...")
florence_model = AutoModelForCausalLM.from_pretrained("microsoft/Florence-2-large", trust_remote_code=True, torch_dtype=torch_dtype).to(device).eval()
florence_processor = AutoProcessor.from_pretrained("microsoft/Florence-2-large", trust_remote_code=True)

# Load a quick, highly accurate MiDaS model pipeline via torch hub
midas = torch.hub.load("intel-isl/MiDaS", "MiDaS_small",trust_repo=True).to(device).eval()
midas_transforms = torch.hub.load("intel-isl/MiDaS", "transforms",trust_repo=True)
transform_depth = midas_transforms.flag_transform if hasattr(midas_transforms, 'flag_transform') else midas_transforms.dpt_transform
    

# 1. Load the specific ControlNet adapter from Hugging Face
controlnet_ids= ["lllyasviel/sd-controlnet-canny","lllyasviel/sd-controlnet-seg","lllyasviel/sd-controlnet-depth"]

controlnet_edge = ControlNetModel.from_pretrained(controlnet_ids[0],torch_dtype=torch.float16)
controlnet_seg = ControlNetModel.from_pretrained(controlnet_ids[1],torch_dtype=torch.float16)
controlnet_depth = ControlNetModel.from_pretrained(controlnet_ids[2],torch_dtype=torch.float16)


pipe_edge = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5", controlnet=controlnet_edge, torch_dtype=torch.float16).to("cuda")

pipe_edge.enable_attention_slicing()

pipe_seg = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5", controlnet=controlnet_seg, torch_dtype=torch.float16).to("cuda")
pipe_seg.enable_attention_slicing()


pipe_depth = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5", controlnet=controlnet_depth, torch_dtype=torch.float16).to("cuda")
pipe_depth.enable_attention_slicing()




@spaces.GPU
def greet(n):
    print(zero.device) # <-- 'cuda:0' 🤗
    return f"Hello {zero + n} Tensor"


IMAGE_FOLDER = "./images"  # Change this to your folder path

@spaces.GPU
def generate_edgeImage(image_path):
    # Convert image to numpy array to generate Canny edges
    init_image = Image.open(image_path).convert("RGB")
    init_image.thumbnail((512, 512), Image.Resampling.LANCZOS)
    image_np = np.array(init_image)
    low_threshold = 100
    high_threshold = 200
    edges = cv2.Canny(image_np, low_threshold, high_threshold)
    # Convert back to PIL format for the pipeline
    canny_image = Image.fromarray(edges).convert("RGB")
    return canny_image
    
@spaces.GPU
def get_florence_segmentation(image, task_prompt="<REFERRING_EXPRESSION_SEGMENTATION>", text_input="the main object"):
    """
    Prompts Florence-2 to isolate an object and map out a clean segmentation mask.
    """
    if task_prompt == "<REFERRING_EXPRESSION_SEGMENTATION>":
        prompt = task_prompt + text_input
    else:
        prompt = task_prompt

    inputs = florence_processor(text=prompt, images=image, return_tensors="pt").to(device, torch_dtype)

    with torch.no_grad():
        generated_ids = florence_model.generate(
            input_ids=inputs["input_ids"],
            pixel_values=inputs["pixel_values"],
            max_new_tokens=1024,
            num_beams=3
        )

    generated_text = florence_processor.batch_decode(generated_ids, skip_special_tokens=False)[0]
    parsed_answer = florence_processor.post_process_generation(
        generated_text,
        task=task_prompt,
        image_size=(image.width, image.height)
    )

    # Extract polygon arrays and build a solid binary conditioning mask image
    mask = Image.new("RGB", image.size, "black")
    try:
        # Pull polygons returned from the segmentation prompt
        polygons = parsed_answer[task_prompt]['polygons']
        from PIL import ImageDraw
        draw = ImageDraw.Draw(mask)
        for poly in polygons:
            # Flatten array for PIL polygon layout [x1, y1, x2, y2...]
            flat_poly = [coord for point in poly for coord in point]
            if len(flat_poly) >= 6:
                draw.polygon(flat_poly, fill="white")
    except KeyError:
        print("Target object segment not clearly resolved. Using fallback empty mask.")

    return mask


@spaces.GPU
def generate_segmt_img(image_path,generate_segmt_img):
    init_image = Image.open(image_path).convert("RGB")
    init_image.thumbnail((512, 512), Image.Resampling.LANCZOS)
    #image_np = np.array(init_image)
    
    conditioning_image = get_florence_segmentation(
        init_image, task_prompt="<REFERRING_EXPRESSION_SEGMENTATION>",
        text_input=generate_segmt_img)
    return conditioning_image

@spaces.GPU
def extract_depth_map(image_path):
    """
    Utilizes a lightweight MiDaS pipeline to extract depth estimation layouts.
    """
    pil_image = Image.open(image_path).convert("RGB")
    pil_image.thumbnail((512, 512), Image.Resampling.LANCZOS)
    cv_img = np.array(pil_image)
    print(f"cv_img: '{cv_img.shape}'")
    img_tensor = transform_depth(cv_img).to(device)

    with torch.no_grad():
        prediction = midas(img_tensor)
        prediction = torch.nn.functional.interpolate(
            prediction.unsqueeze(1),
            size=pil_image.size[::-1],
            mode="bicubic",
            align_corners=False,
        ).squeeze()

    depth_output = prediction.cpu().numpy()
    # Normalize pixel depth values into a visible 0-255 map array
    depth_min, depth_max = depth_output.min(), depth_output.max()
    normalized_depth = (255 * (depth_output - depth_min) / (depth_max - depth_min)).astype(np.uint8)
    depth_rgb = np.concatenate([normalized_depth[:, :, None]] * 3, axis=2)
    return Image.fromarray(depth_rgb)
    
@spaces.GPU   
def get_image_list(folder):
    if not os.path.exists(folder):
        return []
    valid_extensions = (".png", ".jpg", ".jpeg", ".webp", ".gif")
    return [f for f in os.listdir(folder) if f.lower().endswith(valid_extensions)]

image_files = get_image_list(IMAGE_FOLDER)

@spaces.GPU
def load_image(selected_file):
    if not selected_file:
        return None
    return os.path.join(IMAGE_FOLDER, selected_file)

# generate input image 
@spaces.GPU
def generate_input_image(image_path,input_choice,seg_input_promt):
    print(f"image_path: '{image_path}'")
    print(f"input_choice: '{input_choice}'")
    image_path = image_path[0]
    #gc.collect()
    #torch.cuda.empty_cache()
    # ["Edge image","Segmented image","Depth Image"]
    if input_choice == "Edge image" :
        conditioning_image = generate_edgeImage(image_path)
    elif input_choice == "Segmented image" :
        conditioning_image = generate_segmt_img(image_path,seg_input_promt)
    else:
        conditioning_image = extract_depth_map(image_path)
    gc.collect()
    torch.cuda.empty_cache()
    return conditioning_image

@spaces.GPU
def generate_output_image(prompt,conditioning_image,input_choice):
    #---------------------
    if input_choice == "Edge image":
        pipe = pipe_edge
    elif input_choice == "Segmented image" :
        pipe = pipe_seg
    else:
        pipe = pipe_depth
        
    pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.config)
    pipe.enable_attention_slicing()
    #---------------------
    #prompt = "A majestic fantasy castle built into a geometric glass mountain, sharp focus, hyper-detailed digital art"
    negative_prompt = "blurry, low quality, human, text, worst composition"

    generated_output = pipe(
        prompt=prompt,
        negative_prompt=negative_prompt,
        image=conditioning_image,
        num_inference_steps=20,
        guidance_scale=8.0
        ).images[0]
    gc.collect()
    torch.cuda.empty_cache()
    return generated_output
        
    
# Generation pipeline function using both the source image and text prompt
@spaces.GPU
def process_and_generate(selected_file, prompt):
    if not selected_file:
        return None, "Please select an input image first."
    if not prompt:
        return None, "Please enter a text prompt."
    
    input_path = os.path.join(IMAGE_FOLDER, selected_file)
    
    # -------------------------------------------------------------
    # PLACEHOLDER: Insert your Multi-ControlNet / SD pipeline here
    # e.g., output = pipeline(image=input_path, prompt=prompt)
    # -------------------------------------------------------------
    
    # For demonstration, returning the input image path
    generated_output = input_path 
    
    return generated_output, f"Generated matching prompt: '{prompt}'"

with gr.Blocks() as demo:
    gr.Markdown("# Guided Image Generation Pipeline")
    
    with gr.Row():
        # Column 1: Source Image Selection
        with gr.Column():
            gr.Markdown("### 1. Source Image")
            file_input = gr.File(
                label="Select Images or a Folder", 
                file_count="multiple", 
                file_types=["image"]
            )
            seg_input = gr.Textbox( label="segmentation Prompt", 
                placeholder="Describe modifications, styles, or Canny/Depth map directions...")
            with gr.Row():
                image_dropdown = gr.Dropdown(
                    choices=["Edge image","Segmented image","Depth Image"] , 
                    label="Select Input image method",
                    value="Edge image"
                )
                generate_inp_btn = gr.Button("Generate Input", variant="primary")
            
            input_preview = gr.Image(type="pil", label="Input Preview")
            
        # Column 2: Prompt Engineering & Execution
        with gr.Column():
            gr.Markdown("### 2. Generation Settings")
            prompt_input = gr.Textbox(
                label="Text Prompt", 
                placeholder="Describe modifications, styles, or Canny/Depth map directions...",
                lines=3
            )
            generate_btn = gr.Button("Generate Image", variant="primary")
            
        # Column 3: Output Visualization
        with gr.Column():
            gr.Markdown("### 3. Pipeline Output")
            output_image = gr.Image(type="filepath", label="Generated Output")
            status_text = gr.Textbox(label="Status", interactive=False)
            
    # Core event binds
    #image_dropdown.change(
    #    fn=load_image, 
    #    inputs=image_dropdown, 
    #    outputs=input_preview
    #)
    generate_inp_btn.click(
        fn=generate_input_image,
        inputs=[file_input,image_dropdown,seg_input],
        outputs=[input_preview]
    )
    generate_btn.click(
        fn=generate_output_image,
        inputs=[prompt_input,input_preview,image_dropdown],
        outputs=[output_image]
    )
    
    # Initial state setup
    #if image_files:
    #    demo.load(
    #        fn=load_image, 
    #        inputs=image_dropdown, 
    #        outputs=input_preview
    #    )

if __name__ == "__main__":
    demo.launch(share=True)