Ledmush421 commited on
Commit
f4aaf73
Β·
1 Parent(s): cc32495

Add Perchance-style image generator

Browse files
Files changed (2) hide show
  1. app.py +164 -0
  2. requirements.txt +8 -0
app.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import torch
3
+ from diffusers import StableDiffusionXLPipeline, EulerAncestralDiscreteScheduler
4
+ from PIL import Image
5
+ import random
6
+ import os
7
+
8
+ # Model selection based on art style
9
+ MODEL_MAP = {
10
+ "default": "stabilityai/stable-diffusion-xl-base-1.0",
11
+ "anime": "cagliostrolab/animagine-xl-3.1",
12
+ "pixel": "wavymulder/pixel-art-diffusion",
13
+ "cinematic": "stabilityai/stable-diffusion-3.5-medium",
14
+ }
15
+
16
+ class PerchanceStyleGenerator:
17
+ def __init__(self):
18
+ self.current_model = None
19
+ self.pipe = None
20
+
21
+ def load_model(self, model_key="default"):
22
+ if self.current_model != model_key:
23
+ model_id = MODEL_MAP.get(model_key, MODEL_MAP["default"])
24
+ print(f"Loading {model_id}...")
25
+ self.pipe = StableDiffusionXLPipeline.from_pretrained(
26
+ model_id,
27
+ torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
28
+ use_safetensors=True
29
+ )
30
+ self.pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(self.pipe.scheduler.config)
31
+
32
+ if torch.cuda.is_available():
33
+ self.pipe = self.pipe.to("cuda")
34
+ self.current_model = model_key
35
+
36
+ def generate(self, prompt, negative, style, shape, num_images):
37
+ # Map Perchance style to model
38
+ style_lower = style.lower()
39
+ if any(anime in style_lower for anime in ["anime", "manga", "waifu"]):
40
+ model_key = "anime"
41
+ elif "pixel" in style_lower:
42
+ model_key = "pixel"
43
+ elif any(cine in style_lower for cine in ["cinematic", "photo"]):
44
+ model_key = "cinematic"
45
+ else:
46
+ model_key = "default"
47
+
48
+ self.load_model(model_key)
49
+
50
+ # Calculate dimensions based on shape
51
+ if shape == "Square":
52
+ width, height = 1024, 1024
53
+ elif shape == "Portrait":
54
+ width, height = 832, 1216
55
+ else: # Landscape
56
+ width, height = 1216, 832
57
+
58
+ # Enhance prompt with style
59
+ style_prompt = f"{prompt}, {style.lower()} style, high quality, detailed"
60
+
61
+ # Generate images
62
+ images = []
63
+ for i in range(num_images):
64
+ generator = torch.Generator().manual_seed(random.randint(1, 999999))
65
+
66
+ result = self.pipe(
67
+ prompt=style_prompt,
68
+ negative_prompt=negative,
69
+ width=width,
70
+ height=height,
71
+ num_inference_steps=25,
72
+ guidance_scale=7.5,
73
+ generator=generator
74
+ )
75
+ images.append(result.images[0])
76
+
77
+ if len(images) == 1:
78
+ return images[0]
79
+ else:
80
+ return self.make_grid(images, cols=min(4, num_images))
81
+
82
+ def make_grid(self, images, cols=4):
83
+ rows = (len(images) + cols - 1) // cols
84
+ w, h = images[0].size
85
+ grid = Image.new('RGB', (cols * w, rows * h))
86
+ for i, img in enumerate(images):
87
+ grid.paste(img, (i % cols * w, i // cols * h))
88
+ return grid
89
+
90
+ generator = PerchanceStyleGenerator()
91
+
92
+ ART_STYLES = [
93
+ "Painted", "Anime", "Casual Photo", "Cinematic", "Digital Painting",
94
+ "Concept Art", "No style", "3D", "Disney Character", "2D Disney Character",
95
+ "Disney Sketch", "Concept Sketch", "Painterly", "Oil Painting",
96
+ "Oil Painting - Realism", "Oil Painting - Old", "Oil Painting - 70s Pulp",
97
+ "Professional Photo", "Anime Drawn", "Anime Anime", "Anime Screencap",
98
+ "Cute Anime", "Soft Anime", "Fantasy Painting", "Fantasy Landscape",
99
+ "Fantasy Portrait", "Studio Ghibli", "50s Enamel Sign", "Vintage Comic",
100
+ "Franco-Belgian Comic", "Tintin Comic", "Medieval", "Pixel Art",
101
+ "Furry - Oil", "Furry - Cinematic", "Furry - Painted", "Furry - Drawn",
102
+ "Cute Figurine", "3D Emoji", "Illustration", "Cute Illustration",
103
+ "Flat Illustration", "Watercolor", "1990s Photo", "1980s Photo",
104
+ "1970s Photo", "1960s Photo", "1950s Photo", "1940s Photo", "1930s Photo",
105
+ "1920s Photo", "Vintage Pulp Art", "50s Infomercial", "Anime 3D",
106
+ "Pokemon Painted", "2D Pokemon", "Vintage Anime", "Neon Vintage Anime",
107
+ "Manga", "Fantasy World Map", "Fantasy City Map", "Old World Map",
108
+ "3D Isometric", "Icon", "Flat Style Icon", "Flat Style Logo",
109
+ "Game Art Icon", "Digital Painting Icon", "Concept Art Icon",
110
+ "Cute 3D Icon", "Cute 3D Icon Set", "Crayon Drawing", "Pencil",
111
+ "Tattoo Design", "Waifu", "YuGiOh Art", "Traditional Japanese",
112
+ "Nihonga Painting", "Claymation", "Cartoon", "Cursed Photo", "MTG Card"
113
+ ]
114
+
115
+ with gr.Blocks(theme=gr.themes.Soft(), title="ImageMagic") as demo:
116
+ gr.Markdown("# 🌌 ImageMagic")
117
+ gr.Markdown("Like Perchance, but self-hosted on Hugging Face!")
118
+
119
+ with gr.Row():
120
+ with gr.Column(scale=1):
121
+ prompt = gr.Textbox(
122
+ label="πŸ’­ Description",
123
+ placeholder="Describe what you want to see...",
124
+ lines=3
125
+ )
126
+
127
+ negative = gr.Textbox(
128
+ label="🚫 Anti-Description (optional)",
129
+ placeholder="What you DON'T want...",
130
+ lines=2
131
+ )
132
+
133
+ style = gr.Dropdown(
134
+ label="🎨 Art Style",
135
+ choices=ART_STYLES,
136
+ value="Cinematic"
137
+ )
138
+
139
+ with gr.Row():
140
+ shape = gr.Radio(
141
+ label="πŸ–ΌοΈ Shape",
142
+ choices=["Square", "Portrait", "Landscape"],
143
+ value="Square"
144
+ )
145
+
146
+ num_images = gr.Dropdown(
147
+ label="πŸ”’ How many?",
148
+ choices=[1, 2, 4, 6, 8],
149
+ value=1
150
+ )
151
+
152
+ generate_btn = gr.Button("✨ Generate", variant="primary", size="lg")
153
+
154
+ with gr.Column(scale=1):
155
+ output = gr.Image(label="Result", height=500)
156
+
157
+ generate_btn.click(
158
+ fn=generator.generate,
159
+ inputs=[prompt, negative, style, shape, num_images],
160
+ outputs=output
161
+ )
162
+
163
+ if __name__ == "__main__":
164
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ gradio==5.25.0
2
+ torch==2.5.1
3
+ torchvision==0.20.1
4
+ diffusers==0.32.2
5
+ transformers==4.48.3
6
+ accelerate==1.5.2
7
+ pillow==11.1.0
8
+ safetensors==0.5.3